欢迎来到博主的专栏:c++杂谈
博主ID:代码小豪
文章目录
- 万能引用模板
- 右值引用到底是左值还是右值
- 完美转发
万能引用模板
当c++11推出右值引用之后,我们所写的模板函数可以写成这样:
template<class T>
T&& Universal_template(T&& data)
{//......
}
根据直觉判断,这个模板函数的应该是一个右值引用类型的参数和返回值的函数,但是事实真是如此吗?
c++将T&&类型的模板称为万能引用模板,即该参数根据传递的值,可以实例化出左值引用和右值引用两种形式。即使用了万能引用模板的模板函数,可以同时具有接收左值和右值的能力,根据左值或右值的原本类型,该函数模板会实例化出四种版本,分别为:
T& Universal_template(T& data)
{//......
}
const T& Universal_template(const T& data)
{//......
}
T&& Universal_template(T&& data)
{//......
}
const T&& Universal_template(const T&& data)
{//......
}
我们可以用下面的测试来证明这个特性:
template<class T>
void func1(T& data)
{cout << "void func1(T&)" << endl;
}template<class T>
void func1(const T& data)
{cout << "void func1(const T&)" << endl;
}template<class T>
void func1(T&& data)
{cout << "void func1(T&&)" << endl;
}template<class T>
void func1(const T&& data)
{cout << "void func1(const T&&)" << endl;
}template<class T>
void Universal_template(T&& data)
{func1(data);
}
int main()
{int a = 1;//左值const int b = 2;//左值常量Universal_template(a);//int &Universal_template(b);//const int &Universal_template(move(a));//int &&Universal_template(move(b));//const int &&return 0;
}
这段测试的逻辑很简单,就是通过传递不同类型左值和右值,看看万能模板是不是真的能接收左值和右值,并且根据func1模板的实例化结果来判断传递的参数是否正确。
比如传递一个const int的右值,那么Universal_template函数就会实例化成(const int &&)类型的参数,然后就会调用func1(const T &&)的重载版本的函数,然后向屏幕输出"void func1(const T&&)
”以代表万能引用模板确实能接收左值和右值的引用。
根据上面的说法,如果Universal_template函数是一个万能引用模板,那么这段测试的正确输出应该是
void func1(T&)
void func1(const T&)
void func1(T&&)
void func1(constT&&)
但是运行这段测试的真实结果却让人大跌眼镜。
为什么会这样呢?
右值引用到底是左值还是右值
啥?右值引用当然是右值了,难道还能是左值吗?
我只能说,右值引用的引用对象一定是右值,但是右值引用本身不是右值,而是左值。
嘶,这句话咋这么绕呢,我们还是用一个测试来证明吧。
void is_rvalue(int& val)
{cout << "rrc是一个左值" << endl;
}void is_rvalue(int&& val)
{cout << "rrc是一个右值" << endl;
}int main()
{int c = 10;int&& rrc = move(c);//引用对象是右值is_rvalue(rrc);return 0;
}
运行结果为:
这说明在c++中,右值引用的对象是右值,但是右值引用本身是一个变量,因此是一个左值。
完美转发
通过分析可知,右值引用本身是一个左值
是导致前面万能引用模板的测试失效的原因,因为无论是T&&类型的data,还是const T&&类型的data,其本身是一个左值,因此传递给func1函数时,其实例化出的函数只会是const T&,或者T&的类型。
为了解决这个问题,c++推出一个forward()的模板函数、通常我们将这个函数称为“完美转发”
lvalue (1)
template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
rvalue (2)
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;
这个函数的作用是,可以判断该左值到底是左值引用,还是右值引用。(毕竟右值引用本身也是左值)。那么上面那个万能引用模板的问题也随之解决了。
template<class T>
void Universal_template(T&& data)
{func1(forward<T> (data));//如果data是一个右值引用类型,那么传递的也是右值//如果data是一个左值引用类型,那么传递的则是一个左值
}