当rvalue未实现移动时,在具有移动语义的容器上实现insert()

Mad*_*key 4 c++ containers move c++11

我正在尝试实现一个容器,它为insert方法提供复制和移动语义.

实现是这样的:

template <typename T> class Buffer {
    // lots of stuff ommitted here
    ...

    // copy semantics
    Buffer<T>::Iterator push(const T& item) {
        Buffer::Iterator head = push();
        *head = item;
        return head;
    }

    // move semantics
    Buffer<T>::Iterator push(T&& item) {
        Buffer::Iterator head = push();
        *head = std::move(item);
        return head;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果类型T(要推入缓冲区的类型)实现移动赋值运算符,则此方法正常.但是,如果我尝试推送像这样的实例,我会收到编译器错误:

struct Foo {
    Foo(int a) : m_bar(a) {}
    int m_bar;
    Foo& operator=(Foo& other) {
        this.m_bar = other.m_bar;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我尝试编译,buffer.push(Foo(42));我会push(T&& item)在读取的行上获得-method 的编译器错误*head = std::move(item);.错误是没有可行的重载operator=接受rvalue - 这是正确的,没有一个.只有一个赋值操作符接受左值.

但是由于我无法确保每个要存储在容器中的对象都有一个正确实现的移动赋值运算符,我需要确保正确处理这种情况.更重要的是,std :: vector处理这个没有问题.当一个对象实现移动赋值push_back将移动它,如果不实现它将复制它.无论它是否是左值.事实上,如果我把我Foo之前导致错误的有问题的rvalue放到std :: vector中,它的工作方式应该如此.

那我错过了什么?我的容器如何实现移动语义,并且仍然支持对未实现移动分配的对象的rvalue引用?

Pio*_*cki 5

您正在做/假设错误的是复制赋值运算符的错误签名:

Foo& operator=(Foo& other);
Run Code Online (Sandbox Code Playgroud)

它对实例采用非const左值引用other.如果没有用户提供的赋值运算符采用右值引用(也就是说,rvalue可以通过const左值引用绑定),这可以防止移动回退到常规副本中,因此它应该是:

Foo& operator=(const Foo& other);
//             ~~~~^
Run Code Online (Sandbox Code Playgroud)

然后为什么它适用于std::vector<Foo>那么?

buffer.push_back(Foo(42));语句使用复制构造函数,而不是赋值运算符.这是有效的,因为Foo有一个隐式生成的以下签名的拷贝构造函数:

Foo(const Foo&);
Run Code Online (Sandbox Code Playgroud)

适用于左值和左值(DEMO).

你想做什么:

*head = std::move(item);
Run Code Online (Sandbox Code Playgroud)

是使用赋值运算符.既然你已经自己声明了一个,那么编译器就不能隐式生成一个带有const左值引用的编译器,并且它也不能使用用户声明的一个采用非const左值引用,这会导致错误你看到.

考虑在操作中使用allocator或placement-new运算符push,使用item参数作为构造函数的参数Foo,而不是使用复制赋值和移动赋值运算符.