完美转发指的是:如果 function() 函数接收到的参数 t 为左值,那么该函数传递给 otherdef() 的参数 t 也是左值;反之如果 function() 函数接收到的参数 t 为右值,那么传递给 otherdef() 函数的参数 t 也必须为右值。
使用 C++ 98/03 标准下的 C++ 语言,可以采用函数模板重载的方式实现完美转发:
1 |
|
对于右值 5 来说,它实际调用的参数类型为 const T& 的函数模板,由于 t 为 const 类型,所以 otherdef() 函数实际调用的也是参数用 const 修饰的函数,所以输出“rvalue”;
对于左值 x 来说,2 个重载模板函数都适用,C++编译器会选择最适合的参数类型为 T& 的函数模板,进而 therdef() 函数实际调用的是参数类型为非 const 的函数。
此实现方式仅适用于模板函数仅有少量参数的情况,否则就需要编写大量的重载函数模板,造成代码的冗余。
C++11 标准中规定,通常情况下右值引用形式的参数只能接收右值,不能接收左值。但对于函数模板中使用右值引用语法定义的参数来说,它不再遵守这一规定,既可以接收右值,也可以接收左值(此时的右值引用又被称为“万能引用”)。
以 function() 函数为例:
1 | template <typename T> |
此模板函数的参数 t 既可以接收左值,也可以接收右值。
如果调用 function() 函数时为其传递一个左值引用或者右值引用的实参,如下所示:
1 | int n = 10; |
其中,由 function(num) 实例化的函数底层就变成了 function(int & & t),同样由 function(num2) 实例化的函数底层则变成了 function(int && && t)。
C++ 11为其指定了新的类型匹配规则,又称为引用折叠规则(假设用 A 表示实际传递参数的类型):
- 当实参为左值或者左值引用(A&)时,函数模板中 T&& 将转变为 A&(A& && = A&);
- 当实参为右值或者右值引用(A&&)时,函数模板中 T&& 将转变为 A&&(A&& && = A&&)。
只需要知道,在实现完美转发时,只要函数模板的参数类型为 T&&,则 C++ 可以自行准确地判定出实际传入的实参是左值还是右值。
通过将函数模板的形参类型设置为 T&&
,我们可以很好地解决接收左、右值的问题。
但除此之外,还需要解决一个问题,即无论传入的形参是左值还是右值,对于函数模板内部来说,形参既有名称又能寻址,因此它都是左值。那么如何才能将函数模板接收到的形参连同其左、右值属性,一起传递给被调用的函数呢?
C++11 引入了一个模板函数 forword<T>()
,我们只需要调用该函数,就可以很方便地解决此问题
1 |
|
可以看到,forword() 函数模板用于修饰被调用函数中需要维持参数左、右值属性的参数。
总的来说,在定义模板函数时,我们采用右值引用的语法格式定义参数类型,由此该函数既可以接收外界传入的左值,也可以接收右值;其次,还需要使用 C++11 标准库提供的 forword() 模板函数修饰被调用函数中需要维持左、右值属性的参数。由此即可轻松实现函数模板中参数的完美转发。