杂货边角(25):完美转发

完美转发是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