3

我通常(尝试)使用复制交换习语编写异常安全的复制赋值运算符,我想知道在编写移动赋值运算符时是否应该关注异常。以下是复制赋值运算符的示例:

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,感谢您的回答!

4

2 回答 2

5

实际上,如果移动构造函数可能抛出异常,则很难或不可能提供异常保证。

我建议像标准库那样做:记录某些操作仅具有异常保证(或者,在某些情况下,仅允许)如果 move-construction ofT不抛出。通过复制对象来确保保证会破坏所有类型的移动分配的好处,而不仅仅是可能抛出的(非常罕见的)那些。

于 2013-08-29T09:51:00.003 回答
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 讲座:Stephan T. Lavavej - 标准模板库 (STL),9 of n(右值引用)(视频和 STL 的评论和评论中的代码)。

您可能还想阅读 Dave Abrahams 的文章Your Next Assignment...</a> 和Exceptionally Moving!.

于 2013-08-29T10:17:27.607 回答