完美转发是c++11 引入右值引用之后,在template 中的延伸. 顾名思义,完美转发是将参数不改变属性的条件下,转发给下一个函数. 因为普通函数的参数一旦具名,始终都是lvalue. 如果把rvalue转发到下一个函数上的参数中,还是rvalue.这就是完美转发的目的。
#include<iostream> using namespace std; struct X {}; void inner(const X&) {cout << "inner(const X&)" << endl;} void inner(X&&) {cout << "inner(X&&)" << endl;} template<typename T> void outer(T&& t) {inner(forward<T>(t));} int main() { X a; outer(a); outer(X()); inner(forward<X>(X())); } //inner(const X&) //inner(X&&) //inner(X&&)
那么如何支持完美转发呢?第一反应就是用通用引用;假设如此实现:
template<class T> T&& forwarding(T&& t){ return static_cast<T&&>(param); }
传入左值的时候是forward(x), 传入右值是forward(X()),看起来符合要求。但是这里用错了完美转发的具体场景,也就是通常我们用完美转发是什么形式呢?如果不使用完美转发,那么在template function会出现什么情况?
template <typename T> void relay(T&& t) { cout << "in relay" << endl; func(t); }
relay(temp());传入的是右值,但是转发的时候t被认为左值. 不符合完美转发语义
template<typename T> void foo(T&& fparam) { std::forward<T>(fparam); }
以上是完美转发的场景,完美转发配套通用引用,才是它的应用场景, 因为param已经具名,所以是lvalue,刚才的简单实现,显然不符合。
template <typename T> T&& forward(typename remove_reference<T>::type& param) { return static_cast<T&&>(param); }
以上实现,才是完美转发的主要实现,为什么是主要实现,因为它是最多场景的实现。我们分析一下foo(A());T被推导为A,
fparam是lvalue,param是A&。因此匹配
但是如果我们翻C++ 完美转发的实现:貌似还有另外一个实现:
template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept;
这里应对什么场景呢?直接调用forward<A>(A())的场景。 注意这里
typename remove_reference<T>::type&&不再是通用引用,而是右值引用。之前的左值引用,不能接右值,所以需要重定义一个函数.
以上就实现了完美转发的所有内容.完美转发的根基是引用折叠和通用引用.
其他:
template< class T > struct remove_reference {typedef T type;}; template< class T > struct remove_reference<T&> {typedef T type;}; template< class T > struct remove_reference<T&&> {typedef T type;};