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

2023-11-24

class Foo {
  std::vector<SomeType> data_;
};

Say Foo只能通过复制(技术上我的意思是复制或移动)来构建std::vector<SomeType>目的。编写构造函数的最佳方法是什么Foo?

我的第一感觉是

Foo(std::vector<SomeType> data) noexcept : data_(std::move(data)) {};

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


你的第一感觉很好。严格来说这并不是最优的。但它是如此接近最佳,以至于你有理由说你不在乎。

解释:

Foo(std::vector<SomeType> data) noexcept : data_(std::move(data)) {};

当客户端传入左值时std::vector<SomeType>将制作 1 份副本以绑定到data争论。然后将采取 1 步将参数“复制”到data_.

当客户端传入一个xvalue时std::vector<SomeType>将进行 1 次移动以绑定到data争论。然后将采取另一步将论证“复制”到data_.

当客户端传入纯右值时std::vector<SomeType>此举将在约束力中被省略data争论。然后将采取 1 步将参数“复制”到data_.

Summary:

client argument    number of copies     number of moves
  lvalue                  1                   1
  xvalue                  0                   2
  prvalue                 0                   1

如果你这样做:

Foo(const std::vector<SomeType>&  data)          : data_(data) {};
Foo(      std::vector<SomeType>&& data) noexcept : data_(std::move(data)) {};

那么你的性能会稍高一些:

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

当客户端传入一个xvalue时std::vector<SomeType>将采取 1 步将参数“复制”到data_.

当客户端传入纯右值时std::vector<SomeType>将采取 1 步将参数“复制”到data_.

Summary:

client argument    number of copies     number of moves
  lvalue                  1                   0
  xvalue                  0                   1
  prvalue                 0                   1

结论:

std::vector移动结构非常便宜,尤其是相对于副本而言。

当客户端传递左值时,第一个解决方案将花费您额外的一步。与必须分配内存的副本成本相比,这可能是噪音水平。

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

当客户端传递纯右值时,这两种解决方案是等效的。


随着构造函数中参数数量的增加,第二种方案的维护成本呈指数级增长。也就是说,每个参数都需要 const 左值和右值的每种组合。这在 1 个参数(两个构造函数)时非常容易管理,在 2 个参数(4 个构造函数)时则不太容易管理,之后很快就会变得难以管理(8 个构造函数和 3 个参数)。因此,最佳性能并不是这里唯一关心的问题。

如果一个人有很多参数,并且担心左值和x值参数的额外移动构造的成本,还有其他解决方案,但它们涉及相对丑陋的模板元编程技术,许多人认为这些技术太丑陋而无法使用(我不认为,但我试图保持公正)。

For std::vector,额外移动构造的成本通常足够小,您将无法在整体应用程序性能中衡量它。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 C++11 中编写持有 STL 容器的类的构造函数的最佳方法 的相关文章

随机推荐