编写在C++ 11中拥有STL容器的类的构造函数的最佳方法

upd*_*liu 9 constructor move c++11

class Foo {
  std::vector<SomeType> data_;
};
Run Code Online (Sandbox Code Playgroud)

Say Foo只能通过制作一个对象的副本(技术上是指复制或移动)来std::vector<SomeType>构造.编写构造函数的最佳方法是Foo什么?

我的第一感觉是

Foo(std::vector<SomeType> data) noexcept : data_(std::move(data)) {};
Run Code Online (Sandbox Code Playgroud)

使用它,构造实例需要0或1次矢量复制,具体取决于{data}的参数是否可移动.

How*_*ant 17

你的第一感觉是好的.严格来说,这不是最佳选择.但它是如此接近最优,你说你不在乎是合理的.

说明:

Foo(std::vector<SomeType> data) noexcept : data_(std::move(data)) {};
Run Code Online (Sandbox Code Playgroud)

当客户端传入左值时,std::vector<SomeType>将使副本绑定到data参数.然后将进行1次移动以将"复制"参数data_.

当客户端传入xvalue std::vector<SomeType>1时,将移动绑定到data参数.然后将采取另一项举措,将论证"复制"进去data_.

当客户端传入prvalue时std::vector<SomeType>,移动将被省略绑定到data参数.然后将进行1次移动以将"复制"参数data_.

摘要:

client argument    number of copies     number of moves
  lvalue                  1                   1
  xvalue                  0                   2
  prvalue                 0                   1
Run Code Online (Sandbox Code Playgroud)

如果您改为:

Foo(const std::vector<SomeType>&  data)          : data_(data) {};
Foo(      std::vector<SomeType>&& data) noexcept : data_(std::move(data)) {};
Run Code Online (Sandbox Code Playgroud)

然后你的表现会略高一点:

当客户端传入左值std::vector<SomeType>1副本时,将复制参数data_.

当客户端传入xvalue std::vector<SomeType>1时,将进行"复制"参数data_.

当客户端传入prvalue std::vector<SomeType>1时,将进行"复制"参数data_.

摘要:

client argument    number of copies     number of moves
  lvalue                  1                   0
  xvalue                  0                   1
  prvalue                 0                   1
Run Code Online (Sandbox Code Playgroud)

结论:

std::vector 移动结构非常便宜,特别是关于副本的测量.

当客户端传入左值时,第一个解决方案将花费您额外的费用.与必须分配内存的副本的成本相比,这可能是在噪声级别.

当客户端传入xvalue时,第一个解决方案将花费您额外的移动.这可能是解决方案的一个弱点,因为它会使成本翻倍.性能测试是确保这是或不是问题的唯一可靠方法.

当客户端传递prvalue时,两种解决方案都是等效的.


随着构造函数中参数的数量增加,第二个解决方案的维护成本呈指数增长.也就是说,您需要每个参数的const lvalue和rvalue的每个组合.这在1个参数(两个构造函数)中非常易于管理,在2个参数(4个构造函数)中更少,并且在此之后快速变得难以管理(具有3个参数的8个构造函数).因此,最佳性能不是唯一的关注点.

如果一个参数有很多参数,并且担心lvalue和xvalue参数的额外移动构造的成本,还有其他解决方案,但它们涉及相对丑陋的模板元编程技术,许多人认为这些技术太难看了(我不知道) ,但我试图不偏不倚).

因为std::vector,额外移动构造的成本通常很小,您将无法在整体应用程序性能中进行测量.