移动非指针数据成员的语义



可能这个问题已经被问过了,但我不知道该搜索什么。

如果数据成员定义了move赋值操作符,可以将move语义用于非指针数据成员吗?

假设我有一个类M,它这样定义M::operator=(M&&):

template <class T>
class M
{
public: 
    M()
    {
        mem_M = new T;
    }
    M& operator=(M&& src)
    {
        if (this != &src)
        {
            mem_M = src.mem_M;
            src.mem_M = nullptr;
        }
        return *this;
    }
private:    
    T* mem_M;
};

现在显然我可以有一个类C<T>这样,移动构造函数不使用T的移动赋值操作符:

template <class T>
class C
{
public:
    C ()
    {
        mem_C = new T;
    }
    C (C&& rhs)
    {
        mem_C = rhs.mem_C;
        rhs.mem_C = nullptr;
    }
private:
    T* mem_C;
};

然而,如果我想让C<T>::mem_C不是指针而是普通成员,我该如何处理移动函数中的C<T>::mem_C呢?我当然可以调用移动赋值运算符T::operator=(T&&)来将文件mem_C从一个实例移动到另一个实例,但是我如何正确地重置传递给C<T>::C(C&&)C的实例呢?

这至少在我看来是错误的:

template <class T>
class C
{
public:
    C ()
    {
        mem_C = T();
    }
    C (C<T>&& rhs)
    {
        mem_C = std::move(rhs.mem_C);
        rhs.mem_C = T();          // ?? like this?
    }
private:
    T mem_C;
};

那么,在移动函数中重置非指针数据成员的标准兼容方式是什么?

所包含类型的move赋值/构造函数必须使对象处于"可接受"状态,无论这对该类型意味着什么。被移动的类型之外的任何东西都不应该负责维护对象的状态。

另外,你要确保你调用的是父类move 构造函数中包含类型的move 构造函数,而不是包含类型的move 赋值,就像你在上面的例子中:

// move constructor calls move constructor of contained elements
C (C<T>&& rhs) : mem_c(std::move(rhs.mem_c))
{
    // anything in here is using already-constructed data members
}
// move assignment calls move assignment of contained elements
C & operator=(C<T>&& rhs) {
    mem_c = std::move(rhs.mem_c);
}

最新更新