Chapter1 Function Templates
template argument deduction
类型推断期间的类型转换
限制的自动类型转换
template<typename T> T foo(T lhs, T rhs);
foo(2, 2.33); // error
std::string ha = "蛤";
foo("蛤", ha); // error
解决: 1,
foo<int>(2, 2.33); // or foo<double>(2, 2.33);
foo<std::string>("蛤", ha);
2,
foo(2, static_cast<int>(2.33));
foo(static_cast<std::string>("蛤"), ha);
3,
template<typename T, typename U> T foo(T lhs, U rhs);
默认参数的类型推断
template<typename T> void foo(T = "");
foo(); // error: cannot deduce T
解决:
template<typename T = std::string>
void foo(T = "");
f(); // ok
multiple template parameters
问题, 返回值如何推断?
template<typename T, typename U>
T foo(T a, U b)
{
return a > b ? a : b;
}
auto res = foo(2, 4.5);
auto res1 = foo(4.5, 2);
解决: 1,
foo<double>(2, 4.5); // or foo<int>(2, 4.5);
2,
template<typename T, typename U, typename RT>
RT foo(T a, U b);
// call like this
foo<int, int, double>(2, 4.5); // ok, return type is int, but tedious
// improve
template<typename RT, typename T, typename U>
RT foo(T a, U b);
//call like this
foo<int>(2, 4.5); // return type is int, T an dU are deduced
返回值的推断
C++11
template<typename T, typename U> auto foo(T a, U b) -> decltype(a > b ? a : b)
{
return a > b ? a : b;
}
注意
template<typename T, typename U> auto foo(T a, U b) -> decltype(a > b ? a : b)
是一个声明,所以编译器会在编译期找到foo
的返回类型,foo
的实现不必要和其一致,所以可直接携程decltype(true ? a : b)
。
然而,这样声明有一个巨大的缺点: 当底层的实际类型是一个引用时,返回值就是个引用。这样的话你应该返回decay后的类型, 比如:
template<typename T, typename U> auto foo(T a, U b) -> typename std::decay<decltype(true ? a : b)>::type
{
return a > b ? a : b;
}
或者使用C++11提供的std::common_type
替换为-> typename std::common_type<T, U>::type
, std::common_type
总是decay后的。
C++14 你可以这样写
template<typename T, typename U> auto foo(T a, U b)
{
return a > b ? a : b;
}
注意
auto
总是decay的,所以你需要返回引用时,可以这样:
decltype(auto) foo(int& x)
{
x *= x;
return x;
}
default template arguments
template<typename RT = long, typename T, typename U>
RT foo(T a, U b)
{
return a > b ? a : b;
}
注意
和函数的默认参数不一样(函数的默认参数必须放在最后),同时还需要注意,对于可变模板参数
struct Foo {};
template<typename... Args, typename T, typename... Rest>
size_t bar(Args&&..., T&&, Rest&&...)
{
return (sizeof...(Args) + 1 + sizeof...(Rest));
}
int main()
{
std::cout << bar(1, 2, 3, 4) << '\n'; // print 4
std::cout << bar<int, int, Foo>(1, 2, Foo{}, 3, 4) << '\n'; // print 5
}
在没有C++没有正式支持Concepts
之前,默认模板参数经常和std::enable_if
一起用于type constrants
。
overloading function templates
vs2017 bug
work as expect in g++7
template<typename T>
const T& foo(const T& lhs, const T& rhs)
{
return lhs > rhs ? lhs : rhs; // same issue, return local address
}
const char* foo(const char* lhs, const char* rhs)
{
return strcmp(lhs, rhs) ? lhs : rhs;
}
template<typename T>
const T& foo(const T& _1, const T& _2, const T& _3)
{
return ::foo(::foo(_1, _2), _3);
}
int main()
{
const char* it = "it";
const char* he = "he";
const char* she = "she";
auto res = foo(it, he, she); // auto return type always decay
std::cout << res << '\n'; // should cause runtime error
}