Wal*_*ter 17 c++ vector copy-and-swap c++11
自2011年以来,我们同时拥有复制和移动任务.但是,这个答案非常有说服力地说,对于资源管理类,只需要一个赋值运算符.对于std::vector,例如,这看起来像
vector& vector::operator=(vector other)
{
swap(other);
return*this;
}
Run Code Online (Sandbox Code Playgroud)
这里重要的一点是,论证是以价值为基础的.这意味着在输入函数体的时刻,大部分工作已经通过构造来完成other(如果可能的话,通过移动构造函数,否则通过复制构造函数).因此,这会自动正确地实现复制和移动分配.
如果这是正确的,为什么(根据这个文档至少)std::vector 没有以这种方式实现?
编辑以解释这是如何工作的.请考虑other以下示例中的上述代码中发生的情况
void foo(std::vector<bar> &&x)
{
auto y=x; // other is copy constructed
auto z=std::move(x); // other is move constructed, no copy is ever made.
// ...
}
Run Code Online (Sandbox Code Playgroud)
如果元素类型不是可复制的,或者容器不遵守强异常保证,则在目标对象具有足够容量的情况下,复制赋值运算符可以避免分配:
vector& operator=(vector const& src)
{
clear();
reserve(src.size()); // no allocation if capacity() >= src.size()
uninitialized_copy_n(src.data(), src.size(), dst.data());
m_size = src.size();
}
Run Code Online (Sandbox Code Playgroud)
cdo*_*nat -2
实际上定义了三个赋值运算符:
vector& operator=( const vector& other );
vector& operator=( vector&& other );
vector& operator=( std::initializer_list<T> ilist );
Run Code Online (Sandbox Code Playgroud)
您的建议vector& vector::operator=(vector other)使用复制和交换习惯用法。这意味着,当调用该运算符时,原始向量将被复制到参数中,复制向量中的每一项。然后这个副本将与 交换this。编译器可能能够删除该副本,但复制删除是可选的,移动语义是标准的。
您可以使用该习惯用法来替换复制赋值运算符:
vector& operator=( const vector& other ) {
swap(vector{other}); // create temporary copy and swap
return *this;
}
Run Code Online (Sandbox Code Playgroud)
每当复制任何元素抛出异常时,该函数也会抛出异常。
要实现移动赋值运算符,只需省略复制:
vector& operator=( vector&& other ) {
swap(other);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
由于swap()does 永远不会抛出异常,因此移动赋值运算符也不会抛出异常。
-initializer_list赋值也可以通过使用移动赋值运算符和匿名临时来轻松实现:
vector& operator=( std::initializer_list<T> ilist ) {
return *this = vector{ilist};
}
Run Code Online (Sandbox Code Playgroud)
我们使用了移动赋值运算符。因此,initializer_list只有当其中一个元素实例化抛出异常时,赋值操作符才会抛出异常。
正如我所说,编译器可能能够省略副本分配的副本。但编译器没有义务实现该优化。它有义务实现移动语义。
| 归档时间: |
|
| 查看次数: |
825 次 |
| 最近记录: |