C++11中的完美转发

背景问题
我们想要创建创建一个工厂模版函数factory, 使用Arg类型的参数arg,构造一个T对象,并返回它的shared_ptr, 我们的理想是factory就像不存在一样,因此我们需要一个完美转发(perfect forwarding):
- 如果参数是 左值引用,直接以 左值引用 的形式,转发给下一个函数
- 如果参数是 右值引用,要先 “还原” 为 右值引用 的形式,再转发给下一个函数
解决方案
完美转发的两个特定,对应着两个要求
- 必须按引用传递;且左值和右值必须都能引用
- 若引用右值,必须保留右值的特征
对于第一点,通用引用可以完成。
对于第二点,通用引用后,无论是左值引用还是右值引用,他们都是左值,对于左值引用直接转发就可以,但是对于右值引用,我们需要使用static_cast将它转化为一个右值后转发。
函数定义
template<typename T, typename Arg>
std::shared_ptr<T> factory(Arg&& arg) {
return std::shared_ptr<T>(new T(forward<Arg>(arg)));
}
template<typename S>
S&& forward(typename std::remove_reference<S>::type& s) noexcept {
return static_cast<S&&>(s);
}
假设我们有一个类Foo, 拥有一个构造函数Foo(int n)
传递左值
// 当int x = 10; factory<Foo>(x) 传递一个左值时
// Arg&& arg是通用引用, Arg被推导为 int&
std::shared_ptr<Foo> factory(int& arg) {
return std::shared_ptr<Foo>(new Foo(forward<int&>(arg)));
}
// 对于 forward<int&>(arg)
// S被推导为int&
int& && forward(int& s) noexcept {
return static_cast<int& &&>(s);
}
// 进行引用折叠
int& forward(int& s) noexcept {
return static_cast<int&>(s)
}
可以看出:当左值arg引用的是左值时,传递给new Foo()的是被引用的左值(被引用的左值再经过static_cast<Foo&>得到的还是左值引用)。
传递右值
// 当 factory<Foo>(10), 传递一个右值时
// Arg&& 是通用阴影, Arg被推导为 int
std::shared_ptr<Foo> factory(int&& arg) {
return std::shared_ptr<Foo>(new Foo(forward<int>(arg)));
}
// 对于 forward<int>(arg)
// S被推导为 int
int&& forward(int& s) noexcept {
return static_cast<int&&>(s);
}
可以看出:当左值arg引用的是右值时,传递给new T()的是被引用的右值(static_cast把arg强制为右值,因为没有名字)。

Ref
- https://www.yuanguohuo.com/2018/05/25/cpp11-perfect-forward/




