不使用完美转发的缺点/为什么引入完美转发
template<typename T, typename Arg>
shared_ptr<T> factory(Arg arg)
{
return shared_ptr<T>(new T(arg));
}
这个工厂函数,在创建 T对象前,对用户输入的实参数,进行的值拷贝,然后将赋值后的形参传递给了 T 的构造函数。这就不是完美转发。
template<typename T, typename Arg>
shared_ptr<T> factory(Arg& arg)
{
return shared_ptr<T>(new T(arg));
}
这样就解决了值拷贝的问题,但是这也导致 工厂函数无法接受 右值 参数。如factory<X>(41);。
template<typename T, typename Arg>
shared_ptr<T> factory(Arg const & arg)
{
return shared_ptr<T>(new T(arg));
}
在工厂函数的参数加上 const 限制。但是工厂函数的参数不仅仅只有一个,可能会有多个,这样你就需要写多种组合。并且还有一个问题,工厂函数的形参都是左值,无法调用 T 类的移动语意函数。
因此,还会有完美转发的概念提出来。
完美转发
首先了解一个 引用折叠规则
A& &becomesA&A& &&becomesA&A&& &becomesA&A&& &&becomesA&&
现在让我们再看一下之前的例子
template<typename T, typename Arg>
shared_ptr<T> factory(Arg&& arg)
{
return shared_ptr<T>(new T(std::forward<Arg>(arg)));
}
template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{
return static_cast<S&&>(a);
}
例子1,传递左值:
X x;
factory<A>(x);
首先 factory 函数 Arg&& arg 推导成 X& arg,左值引用。
shared_ptr<A> factory(X& && arg)
{
return shared_ptr<A>(new A(std::forward<X&>(arg)));
}
X& && forward(typename remove_reference<X&>::type& a) noexcept
{
return static_cast<X& &&>(a);
}
所以最后传递给 A 的构造函数是一个左值 没有改变属性。
例子2,传递右值:
X foo();
factory<A>(foo());
shared_ptr<A> factory(X&& arg)
{
return shared_ptr<A>(new A(std::forward<X>(arg)));
}
X&& forward(X& a) noexcept
{
return static_cast<X&&>(a);
}
所以最后传递给 A 的构造函数是一个右值 没有改变属性。