你的第一感觉很好。严格来说这并不是最优的。但它是如此接近最佳,以至于你有理由说你不在乎。
解释:
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
,额外移动构造的成本通常足够小,您将无法在整体应用程序性能中衡量它。