我通常(尝试)使用copy-swap惯用法编写异常安全副本赋值运算符,我想知道在编写移动分配运算符时是否应该关注异常。 以下是复制分配运算符的示例:
template<class T>
CLArray<T>&
CLArray<T>::operator=( const CLArray& rhs )
{
CLArray tmp( rhs );
std::swap( size_, tmp.size_ );
std::swap( data_, tmp.data_ );
return *this;
}
但是移动分配怎么样?我的意思是,如果在此移动操作期间代码中的其他位置抛出异常,我将失去两个对象的状态吗?所以我必须首先创建一个本地副本,然后删除除了新创建的CLArray
...
template <class T>
CLArray<T>&
CLArray<T>::operator=( CLArray<T>&& rhs )
{
size_ = rhs.size_;
data_ = std::move( rhs.data_ );
return *this;
}
请注意data_
是std :: vector,感谢您的答案!
答案 0 :(得分:4)
实际上,如果移动构造函数可能抛出异常保证,则可能很难或不可能。
我建议像标准库那样做:记录某些操作只有异常保证(或者,在某些情况下,只允许),如果T
的移动构造没有抛出。通过复制对象来确保保证会破坏所有类型的移动分配的好处,而不仅仅是(非常罕见的)可能抛出的类型。
答案 1 :(得分:3)
无论如何,您应该添加swap
成员函数并利用(复制/移动)赋值运算符中的(复制/移动)构造函数。 (并将那些不可能抛出的操作放在那些可能的操作之后。)
示例(此处内容为简洁类):
template<typename T>
class CLArray {
public:
void swap( CLArray& other )
{
std::swap( data_, other.data_ );
std::swap( size_, other.size_ );
}
CLArray( const CLArray& other )
: data_( other.data_ ), size_( other.size_ )
{
}
CLArray& operator=( const CLArray& rhs )
{
CLArray( rhs ).swap( *this );
return *this;
}
CLArray( CLArray&& other )
: data_( std::move( other.data_ ) )
{
size_ = other.size_;
other.size_ = 0;
}
CLArray& operator=( CLArray&& rhs )
{
CLArray( std::move( rhs ) ).swap( *this );
return *this;
}
// ...
private:
std::vector<T> data_;
std::size_t size_;
};
请参阅C9 Lectures: Stephan T. Lavavej - Standard Template Library (STL), 9 of n (rvalue references)(视频和STL的评论和评论中的代码)。
您可能还想阅读Dave Abrahams的文章Your Next Assignment…和Exceptionally Moving!。