什么是完美转发?
它指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。
template<typename T>
void func_A(T t) {
func_B(t);
}
如果 func_A() 函数接收到的参数 t 为左值,那么该函数传递给 func_B() 的参数 t 也是左值;反之如果 func_A() 函数接收到的参数 t 为右值,那么传递给 func_B() 函数的参数 t 也必须为右值。
在c++98/03中,也可以实现完美转发,但是需要重载两次函数模板
#include <iostream>
using namespace std;
//重载被调用函数,查看完美转发的效果
void func_B(int & t) {
cout << "lvalue\n";
}
void func_B(const int & t) {
cout << "rvalue\n";
}
//重载函数模板,分别接收左值和右值
//接收右值参数
template <typename T>
void func_A(const T& t) {
func_B(t);
}
//接收左值参数
template <typename T>
void func_A(T& t) {
func_B(t);
}
int main()
{
func_A(5);//5 是右值
int x = 1;
func_A(x);//x 是左值
return 0;
}
使用重载的模板函数实现完美转发是有弊端的,仅适用于模板函数仅有少量参数的情况,否则就需要编写大量的重载函数模板,造成代码的冗余。
C++11 标准中规定,通常情况下右值引用形式的参数只能接收右值,不能接收左值。
但对于函数模板中使用右值引用语法定义的参数来说,它不再遵守这一规定,既可以接收右值,也可以接收左值(此时的右值引用又被称为“万能引用”)。
template <typename T>
void func_A(T&& t) { //&&
func_B(t);
}
这时模板的参数t既可以接受左值也可以接受右值。
C++ 11标准为了更好地实现完美转发,特意为其指定了新的类型匹配规则,又称为引用折叠规则:
int n = 10;
int & num = n;
func_A(num); // T 为 int&
int && num2 = 11;
func_A(num2); // T 为 int &&
- 当实参为左值或者左值引用(T&)时,函数模板中 T&& 将转变为 T&(T& && = T&);
- 当实参为右值或者右值引用(T&&)时,函数模板中 T&& 将转变为 T&&(T&& && = T&&)。
这时就可以识别传递进来的参数是左值还是右值了。
模板的问题解决了,那么函数如何接受到这个参数呢?传递进来的参数因为有了名字可以寻址,变为左值了。
我们可以在调用函数的时候也添加&&,如:
template <typename T>
void func_A(T&& t) {
func_B((T&&)t);
}
为了提高可读性,c++11提供了一个模板函数 forward
template <typename T>
void func_A(T&& t) {
func_B(forward<T>(t));
}
fordward
template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept
{ // forward an lvalue as either an lvalue or an rvalue
return (static_cast<_Ty&&>(_Arg));
}
template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept
{ // forward an rvalue as an rvalue
static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
return (static_cast<_Ty&&>(_Arg));
}
评论区