class Foo { std::vector<SomeType> data_; };
说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)) {};
当客户端通过lvalue std :: vector< SomeType> 1复制将绑定到数据参数.然后1动作将“参数”复制到data_中.
当客户端传入一个xvalue std :: vector< SomeType> 1移动将绑定到数据参数.然后另一个动作将“参数”复制到data_中.
当客户端通过prvalue std :: vector< SomeType>移动将在绑定到数据参数时被消除.然后1动作将“参数”复制到data_中.
概要:
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)) {};
那么你有一个非常稍高的性能:
当客户端通过lvalue 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
结论:
std :: vector移动构造非常便宜,特别是对于副本而言.
第一个解决方案将在客户端通过一个左值时花费额外的动作.这可能是在噪音级别,与必须分配内存的副本的成本相比.
当客户端通过xvalue时,第一个解决方案将会花费额外的动作.这可能是解决方案的一个弱点,因为它的成本增加了一倍.性能测试是唯一可靠的方法,以确保这是或不是一个问题.
当客户端通过prvalue时,这两个解决方案是相当的.
随着构造函数中的参数数量的增加,第二个解决方案的维护成本呈指数增长.那就是每个参数需要const lvalue和rvalue的每个组合.这在1个参数(两个构造函数)中非常易于管理,在2个参数(4个构造函数)上较少,并且在此之后迅速变得无法管理(8个具有3个参数的构造函数).因此,最佳性能不是唯一关心的问题.
如果有许多参数,并且关注lvalue和xvalue参数的额外移动构造的成本,还有其他解决方案,但它们涉及相对较丑的模板元编程技术,许多人认为这太难以使用(我不,但我试图无偏见).
对于std :: vector,额外的移动构造的成本通常足够小,您将无法在整体应用程序性能中进行测量.