vso*_*tco 25 c++ rvalue-reference move-semantics c++11
最常见的用法std::forward是,完美地转发转发(通用)引用,例如
template<typename T>
void f(T&& param)
{
g(std::forward<T>(param)); // perfect forward to g
}
Run Code Online (Sandbox Code Playgroud)
这param是一个lvalue,并std::forward最终将它转换为右值或左值,具体取决于与它有关的参数.
看一下cppreference.com的定义,std::forward我发现还有一个rvalue重载
template< class T >
T&& forward( typename std::remove_reference<T>::type&& t );
Run Code Online (Sandbox Code Playgroud)
任何人都可以给我任何rvalue超载的理由吗?我看不到任何用例.如果你想将一个右值传递给一个函数,你可以按原样传递它,不需要std::forward在它上面应用.
这与std::move我不同,我明白为什么人们也想要一个rvalue重载:你可能会处理通用代码,在这些代码中你不知道你传递了什么,你想要无条件支持移动语义,请参阅例如为什么std ::移动采取普遍参考?.
这个答案是为了回答@vsoftco的评论
@DarioOO 感谢您的链接。你能写一个简洁的答案吗?从你的例子来看,我仍然不清楚为什么 std::forward 还需要为右值定义
简而言之:
因为如果没有右值专门化,以下代码将无法编译
#include <utility>
#include <vector>
using namespace std;
class Library
{
vector<int> b;
public:
// hi! only rvalue here :)
Library( vector<int>&& a):b(std::move(a)){
}
};
int main()
{
vector<int> v;
v.push_back(1);
A a( forward<vector<int>>(v));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是我无法抗拒输入更多内容,因此这也是答案的不简洁版本。
长版:
您需要移动,v因为该类Library没有接受左值的构造函数,而只有右值引用。如果没有完美的转发,我们最终会出现不良行为:
当传递重物时,包装函数会导致高性能损失。
通过移动语义,我们确保在可能的情况下使用移动构造函数。在上面的例子中,如果我们删除std::forward代码将无法编译。
那么实际上在做什么呢forward?未经我们同意就移动元素?没有!
它只是创建向量的副本并移动它。我们如何确定这一点?只需尝试访问该元素即可。
vector<int> v;
v.push_back(1);
A a( forward<vector<int>>(v)); //what happens here? make a copy and move
std::cout<<v[0]; // OK! std::forward just "adapted" our vector
Run Code Online (Sandbox Code Playgroud)
如果你移动该元素
vector<int> v;
v.push_back(1);
A a( std::move(v)); //what happens here? just moved
std::cout<<v[0]; // OUCH! out of bounds exception
Run Code Online (Sandbox Code Playgroud)
因此,需要重载才能实现仍然安全的隐式转换,但如果没有重载则不可能。
事实上,以下代码将无法编译:
vector<int> v;
v.push_back(1);
A a( v); //try to copy, but not find a lvalue constructor
Run Code Online (Sandbox Code Playgroud)
真实用例:
您可能会认为转发参数可能会创建无用的副本,从而隐藏可能的性能影响,是的,这确实是事实,但请考虑实际用例:
template< typename Impl, typename... SmartPointers>
static std::shared_ptr<void>
instancesFactoryFunction( priv::Context * ctx){
return std::static_pointer_cast<void>( std::make_shared<Impl>(
std::forward< typename SmartPointers::pointerType>(
SmartPointers::resolve(ctx))...
) );
}
Run Code Online (Sandbox Code Playgroud)
代码取自我的框架(第 80 行):Infectorpp 2
在这种情况下,参数是从函数调用转发的。SmartPointers::resolve无论构造函数Impl接受右值还是左值,它的返回值都会被正确移动(因此不会出现编译错误,并且无论如何都会被移动)。
基本上你可以std::foward在任何你想让代码更简单、更具可读性的情况下使用,但你必须记住两点
如果小心使用,它是一个强大的工具。