自2011年以来,我们同时拥有复制和移动任务。然而,this answer非常令人信服地认为,对于资源管理类,只需要一个赋值运算符。例如,对于std::vector
,这将如下所示
vector& vector::operator=(vector other)
{
swap(other);
return*this;
}
这里重要的一点是,参数是通过值来获取的。这意味着在进入函数体的那一刻,other
的构造已经完成了大部分工作(如果可能,通过移动构造函数,否则通过复制构造函数)。因此,这会自动正确地实现复制和移动分配。
如果这是正确的,为什么(根据this documentation at least) 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.
// ...
}
发布于 2015-11-21 07:39:41
如果元素类型是非抛出可复制的,或者容器不遵守强异常保证,那么在目标对象有足够容量的情况下,复制赋值操作符可以避免分配:
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();
}
发布于 2015-11-21 07:28:58
实际上定义了三个赋值运算符:
vector& operator=( const vector& other );
vector& operator=( vector&& other );
vector& operator=( std::initializer_list<T> ilist );
您的建议vector& vector::operator=(vector other)
使用复制和交换习惯用法。这意味着,当调用运算符时,原始向量将被复制到参数中,复制向量中的每一项。然后,此副本将与this
交换。编译器也许能够省略复制,但复制省略是可选的,移动语义是标准的。
你可以用这个习惯用法来代替复制赋值操作符:
vector& operator=( const vector& other ) {
swap(vector{other}); // create temporary copy and swap
return *this;
}
每当复制任何元素抛出时,此函数也会抛出。
要实现移动赋值操作符,只需省略复制:
vector& operator=( vector&& other ) {
swap(other);
return *this;
}
由于swap()
从不抛出,因此移动赋值操作符也不会抛出。
initializer_list
-assignment还可以通过使用移动赋值操作符和匿名临时操作符来轻松实现:
vector& operator=( std::initializer_list<T> ilist ) {
return *this = vector{ilist};
}
我们使用了移动赋值运算符。作为一个串联,initializer_list
赋值operatpr只会在一个元素实例化抛出时抛出。
正如我所说的,编译器也许能够省略副本赋值的副本。但是编译器没有义务实现这种优化。它必须实现移动语义。
https://stackoverflow.com/questions/33837485
复制相似问题