【C++编程】模板实参推断、重载与模板
模板实参推断
一、模板函数显示实参
情况1:
template <typename T1, typename T2, typename T3> T1 sum(T2 a, T3 b) { return a + b; }
分析:调用的时候就需要指定T1的类型,如:sum<float>(1, 2); 于是sum函数的返回类型为float。
情况2:
template <typename T1, typename T2, typename T3> //糟糕的设计,用户必须指定所有的三个模板参数 T3 sum(T1 a, T2 b) { return a + b; }
分析:对于sum<float>(1, 2); 现在这个调用里指定T1类型为float,但是实际传进来的是1(int类型),会进行隐式类型转换,将1转换为float. T2的类型也可以根据sum(1,2)调用的第二个实参推断出来,这里是可能会是int. 那么T3是什么类型呢?显然这里编译器无法推断T3的类型,需要在调用时指定才能推断:
1. sum<int, int, int>(1, 2); 这样T3就推断出来是int。
2. 在指定显示模板实参时指定的类型是和模板参数匹配的,顺序是一一对应的,如:
• 使用 sum<int>(1, 2); 对上面的第一个模板进行调用,那么T1对应int,T2和T3则通过推断得出。
• 使用 sum<int>(1, 2); 对上面的第二个模板进行调用,那么T1对应的类型是int,T2可以根据实际穿进去的参数进行推断,这里2为int,那么T2类型就是int,那么编译器就无法知道T3的实际类型了。
二、完美转发
template<class T> void wrapper(T&& arg) { // arg 始终是左值 foo(std::forward<T>(arg)); // 转发为左值或右值,依赖于 T } 分析: 1. 若对 wrapper() 的调用传递右值string ,则推导 T 为 std::string(非string& 或string&& ,且 std::forward 确保将右值引用传递给 foo. 2. 若对 wrapper() 的调用传递 const 左值string ,则推导 T 为 const string& ,且 std::forward 确保将 const 左值引用传递给 foo. 3. 若对 wrapper() 的调用传递非 const 左值string ,则推导 T 为string& ,且 std::forward 确保将非 const 左值引用传递给 foo.
分析:
1. 若对 wrapper() 的调用传递右值string ,则推导 T 为 std::string(非string& 或string&& ,且 std::forward 确保将右值引用传递给 foo.
2. 若对 wrapper() 的调用传递 const 左值string ,则推导 T 为 const string& ,且 std::forward 确保将 const 左值引用传递给 foo.
3. 若对 wrapper() 的调用传递非 const 左值string ,则推导 T 为string& ,且 std::forward 确保将非 const 左值引用传递给 foo.
举例
1 #include<iostream> 2 #include<utility> 3 using namespace std; 4 5 void g(int &&i, int &j) 6 { 7 cout << i << " " << j << endl; 8 } 9 10 void f(int v1, int &v2) 11 { 12 cout << v1 << " " << ++v2 << endl; 13 } 14 15 //flip1实现不完整:顶层const和引用都丢掉了 16 template <typename F, typename T1, typename T2> 17 void flip1(F f, T1 t1, T2 t2) 18 { 19 f(t2, t1); 20 } 21 22 template <typename F, typename T1, typename T2> 23 void flip2(F f, T1 &&t1, T2 &&t2) 24 { 25 f(t2, t1); 26 } 27 28 template <typename F, typename T1, typename T2> 29 void flip(F f, T1 &&t1, T2 &&t2) 30 { 31 f(std::forward<T2>(t2), std::forward<T1>(t1)); 32 } 33 34 int main() 35 { 36 int i = 0, j = 0, k = 0, l = 0; 37 cout << i << " " << j << " " << k << " " << l << endl; 38 f(42, i); //f改变其实参i 39 flip1(f, j, 42); //通过flip1调用f不会改变j 40 flip2(f, k, 42); //正确:k被改变了 41 g(42, i); 42 flip(g, i, 42); //正确:第三个参数的右值属性被保留了 43 cout << i << j << " " << k << " " << l << endl; 44 return 0; 45 }