此答案用于回答@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;
}
但是我忍不住要输入更多,所以这也是答案的不简洁版本。
加长版:
您需要移动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
如果您改为移动该元素
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
因此需要重载才能实现仍然安全的隐式转换,但如果没有重载则不可能。
实际上下面的代码将无法编译:
vector<int> v;
v.push_back(1);
A a( v); //try to copy, but not find a lvalue constructor
实际用例:
您可能会争辩说,转发参数可能会创建无用的副本,从而隐藏可能的性能损失,是的,这实际上是正确的,但请考虑实际用例:
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))...
) );
}
代码取自我的框架(第 80 行):Infectorpp 2
在这种情况下,参数是从函数调用中转发的。无论Impl 的构造函数接受右值还是左值,SmartPointers::resolve 的返回值都会被正确移动(因此没有编译错误,并且无论如何都会被移动)。
基本上你可以在任何情况下使用std::foward,以使代码更简单、更易读,但你必须记住两点
- 额外的编译时间(实际上没有那么多)
- 可能会导致不需要的副本(当您没有明确地将某些内容移动到需要右值的内容中时)
如果小心使用是一个强大的工具。