我应该依赖复制省略还是移动语义



给定一个向量

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,在移动的地方生成了零个)。

最新更新