C++11 :完美转发

108 阅读1分钟

不使用完美转发的缺点/为什么引入完美转发

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 类的移动语意函数。

因此,还会有完美转发的概念提出来。

完美转发

首先了解一个 引用折叠规则

  1. A& & becomes A&
  2. A& && becomes A&
  3. A&& & becomes A&
  4. A&& && becomes A&&

现在让我们再看一下之前的例子

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 的构造函数是一个右值 没有改变属性。