给定一个向量
std::vector<BigObject> v;
和工厂功能
BigObject genBigObject();
我想避免复制BigObject
实例。
哪一个更快?
v.push_back(genBigObject());
或
v.push_back(std::move(genBigObject()));
我能依靠复制省略会一直发生的事实吗?(我可以删除BigObject
上的复制构造函数,但是,好吧...
std::move()
的目的是获取一个左值并将其视为右值,以便为未来的其他函数明确表示,如果他们愿意,它们可以蚕食对象的内部。
genBigObject()
已经是一个右值。你不需要move()
它来使其成为一个 - move()
在那里根本没有为你提供任何有价值的东西。所以不要这样做。你甚至不需要进入下游实际发生的问题 - move()
是代码和读者的信号,表明你正在做一些可能不安全的事情,但在这种情况下......你不是。
对于此特定情况,您无论如何都要调用 push_back()
的右值引用重载 - 这会触发临时对象的临时具体化。这种暂时的实现是由于对push_back()
的特别调用还是对move()
的稍早的调用而发生的,都没有区别。
Elision 不会在那里发生,有关详细信息,请参阅 @Barry 的回答。
简而言之,push_back
不支持省略;它采用右值引用或常量左值引用。
你有一个实际的右值;调用std::move
会将该右值转换为右值引用,这是一个毫无意义的操作。 这就像int x = 7; static_cast<int&>(x);
——几乎是无知,有时甚至是悲观。
解决您的核心问题,即您想要elision,我们可以做点什么。
您可以通过一些工作来避免BigObject
的构造:
template<class F>
struct emplacer {
F f;
operator std::result_of_t<F&&()>()&&{ return std::forward<F>(f)(); }
operator std::result_of_t<F&()>()&{ return f(); }
};
template<class T>
emplacer(T&&)->emplacer<T&&>;
现在您可以执行此操作:
v.emplace_back( emplacer{ &getBigObject });
并且BigObject
将直接放入向量缓冲区中(除非过于贪婪的BigObject
构造函数)。
实时示例(请注意完全缺乏输出;我们在向量中生成了 100 个 BigObject,在移动的地方生成了零个)。