完美转发是C++11借助右值引用这个引入的特性而顺带完成的一个很美的特性,涉及到两个重要的概念:引用折叠和模板参数推导。html
#include <iostream>
#include <type_traits>
using namespace std;
void RunCode(int && m ) { cout << "rvalue ref" << endl; }
void RunCode(int & m) { cout << "lvalue ref" << endl; }
void RunCode(const int && m) { cout << "const rvalue ref" << endl; }
void RunCode(const int & m) { cout << "const lvalue ref" << endl; }
template <typename T>
void PerfectForward(T && t) { RunCode(forward<T>(t)); } //C++标准规定,具名的右值引用看成左值,故而这里必须得有forward<T>强制类型转换在,
//否则直接写成RunCode(t)那么其中的t将被看作左值
template <typename T, typename U>
void PerfectForward(T && t, U& Func) {
cout << t << "\tforward..." << endl;
Func(forward<T>(t));
}
void RunPart(double&& m) { cout << "get inside " << __func__ << endl; }
void RunHome(double&& h) { cout << "get inside " << __func__ << endl; }
void RunComp(double&& c) { cout << "get inside " << __func__ << endl; }
// 1. the return type (bool) is only valid if T is an integral type:
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
is_odd (T i) {return bool(i%2);}
// 2. the second template argument is only valid if T is an integral type:
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}
int main()
{
int a;
int b;
const int c = 1;
const int d = 0;
PerfectForward(a);
PerfectForward(move(b));
PerfectForward(c);
PerfectForward(move(d));
PerfectForward(1.5, RunComp); //不借助typeList‘< >’进行特化,而是直接传入参数进行特化
PerfectForward(8, RunPart);
PerfectForward(1.5, RunHome);
int i = 1;
double j = 1.0;
cout << std::boolalpha; //声明ostream中的全部bool量用true/false表示而非1/0,类比std::hex
cout << "i is odd: " << is_odd(i) << endl;
cout << "i is even: " << is_even(i) << endl;
//cout << "j is odd: " << is_odd(j) << endl; //找不到匹配的函数
//cout << "j is even: " << is_even(j) << endl; //由于模板is_even的第二个参数无效,故而没法正常实例化,从而出现找不到匹配的函数for call to 'is_even(double&)`
}
/************************************************** **完美转发是一个很棒的机制。对于完美转发而言,右值引用并不是“天生神力”,只是C++11引入了右值 **概念,所以为其新定了引用折叠的规则,以知足完美转发的需求 **************************************************/
这里面最值得说的以及最容易混淆的莫过于引用折叠,这篇文章有很好的解释。举个简单的常见来讲明:ios
//完美转发函数
template <typename T>
void PerfectForward(T && t) {
T temp;
//to-do ...
}
...
int a = 1; PerfectForward(a);
int& la = a; PerfectForward(la);
int&& ra = 10; PerfectForward(ra);
//依次用左值,左值引用,右值引用调用转发函数,则T&& t实际的类型,以及模板参数T对应的类型规则以下:
模板形参t被声明的类型 | 调用时实际传入的实参类型 | 调用后形参t的实际类型 | 调用后T的实际类型 |
---|---|---|---|
T& | A& or A | A& | A |
T& | A&& | A& | A |
T&& | A& or A | A& | A& |
T&& | A&& | A&& | A |
其中值得注意的是,当采用完美转发特点&&解析模板形参时,调用时实参为左值时和为左值引用的状况一致。web