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

时间:2019-01-30 14:11:14

标签: c++ c++17

给出一个向量

std::vector<BigObject> v;

和工厂功能

BigObject genBigObject();

我想避免复制BigObject实例。

哪个更快?

v.push_back(genBigObject());

v.push_back(std::move(genBigObject()));

我能否依靠复制省略会一直发生的事实? (我可以删除BigObject上的副本构造函数,但是,好吧...)

2 个答案:

答案 0 :(得分:10)

std::move()的目的是采用一个 lvalue 并将其视为 rvalue ,以明确表示其后的其他功能可以蚕食如果他们愿意,可以查看对象的内部。

genBigObject()已经是一个右值。您无需move()就可以使其成为一体-move()根本没有为您提供任何有价值的东西。因此,请不要这样做。您甚至不需要研究下游实际发生什么问题-move()可以向代码和读者发出信号,表明您正在做某些可能不安全的事情,但是在这种情况下...

对于这种特定于 的情况,无论如何,您都在调用push_back()的右值引用重载-这会触发临时对象的临时实现。临时实现具体是由于对push_back()的调用还是对move()的较早调用而没有关系。

答案 1 :(得分:2)

此处不会发生清除,有关详细信息,请参见@Barry的答案。

简而言之,push_back不支持省略号;它需要一个右值引用或一个常量左值引用。

您有一个实际值;调用std::move会将该右值强制转换为右值引用,这是毫无意义的操作。就像int x = 7; static_cast<int&>(x);一样-几乎是无意识的,有时是悲观的。

要解决您的核心关切,即您想省事,我们可以做些什么。

您可以通过一些工作来消除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构造函数)。

Live example(请注意,完全没有输出;我们在向量中生成了100个BigObject,在移动时生成了零)。