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>::typestd::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    
}

questions

passs by value or by refrence ?

why not inline ?

why not constexpr ?