完美转发

所谓完美转发(perfect formarding),是指在函数模板中,彻底依照模板的参数的类型,将参数传递给函数模板中调用的另一个函数。好比:程序员

template <typename T>
void IamForwording(T t)
{
    IrunCodeActually(t);
}

这个简单的例子中,IamForWording是一个转发函数模板。而函数IrunCodeActually则是真正执行代码的目标函数。对于目标函数而言,老是但愿转发函数将参数按照传入时的类型传递,而不产生额外的开销,就好像转发者不存在同样。函数

这彷佛是一件很是简单的事情,但实际却不容易。在上例中,我再IamForwording的参数中使用了最基本类型进行转发,该方法会致使参数在传给IrunCodeActually以前就产生了一次额外的临时对象拷贝。所以这样的转发智能说是正确的转发,但谈不上完美。ui

因此一般程序员须要的是一个引用类型,引用类型不会有拷贝的开销。其次,则须要考虑转发函数对类型的接受能力。由于目标函数可能须要可以既接受左值引用,又接受右值引用,那么若是转发函数智能接受其中的一部分,咱们也没法作到完美转发。咱们可能会想到“万能”的常量左值类型。不过以常量左值为参数的类型函数却会遇到一些尴尬,好比:spa

void IrunCodeActually(int t){}
template <typename T>
void IamForwording(const T &T)
{
    IrunCodeActually(t);
}

这里,因为目标函数的参数类型是很是量左值引用类型,所以没法接受常量左值引用做为参数,这样一来,虽然转发函数的接受能力很高,但在目标函数的接受上却出了问题。若是咱们的目标函数的参数是一个右值引用的,一样没法接受任何左值类型做为参数。.net

那C++11是如何解决完美转发的问题的呢?实际上,C++11是经过引入一条所谓“引用折叠”的新语言规则,并结合新的模板推导规则来完成完美转发。code

在C++11之前,形以下列语句:orm

typedef const int T;
typedef T& TR;
TR &v = 1;

其中TR&v = 1这样的表达式会被编译器认为是不合法的表达式,而在C++11中,一旦出现了这样的表达式,就会发生引用折叠,即将复杂的未知表达式折叠为已知的简单表达式,具体以下:对象

这个规则不难记忆,由于一旦定义中出现了左值引用,引用折叠老是优先将其折叠为左值引用。而模板对类型的推导规则就比较简单,当转发函数的实参是类型X的一个左值引用,那么模板参数被推导为X&类型,而转发函数的实参是类型X的一个右值引用的话,那么模板的参数被推到为X&&类型。结合以上的引用折叠规则,就能肯定出参数的实际类型。进一步,咱们能够把转发函数写成以下形式:blog

template <typename T>
void IamForwording(T&&t)
{
    IrunCodeActually(static_cast<T && >(t);
}

注意一下,咱们不只在参数部分使用了T&&这样的标识,在目标函数传参的强制类型转换中也使用了这样的形式。好比咱们调用转发函数时传入了一个X类型的左值引用,能够想象,转发函数将被实例化为以下形式:get

void IamForwording( X& &&t)
{
    IrunCodeActually(static_cast<X& && >(t);
}

应用上引用折叠规则,就是:

void IamForwording( X&)
{
    IrunCodeActually(static_cast<X& >(t);
}

这样一来咱们的左值传递就毫无问题了。实际使用的时候,IrunCodeActually若是接受左值引用的话,就能够直接调用转发函数。不过读者可能发现,这样调用前的static_cast没有什么做用。事实上,这里的static_cast是留给传递右值用的。

而若是咱们调用转发函数时传入一个X类型的右值引用的话,咱们的转发函数将被实例化为:

void IamForwording( X&& &&t)
{
    IrunCodeActually(static_cast<X&& && >(t);
}

应用上应用折叠规则,就是:

void IamForwording( X&&t)
{
    IrunCodeActually(static_cast<X&& >(t);
}

这里咱们就看到了static_cast的重要性。对于一个右值而言,当它使用右值引用表达式引用的使用,该右值确实一个彻彻底底的左值,那么咱们想在函数调用中继续传递右值,就须要使用std::move来进行左右值的转换。而std::move一般就是一个static_cast。不过在C++11中,用于完美转发的函数却不是move,而是另一个名字:forward。因此咱们能够把转发函数写成这样:

template <typename T>
void IamForwording( T &&t)
{
    IrunCodeActually(forword(t));
}

参考:《深刻理解C++11》