Implementing insert () in a container with move semantics when rvalue does not implement move

I am trying to implement a container that provides paste methods with copy and move semantics.

The implementation looks something like this:

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;
    }
}

      

This works great if the type T (the type to be inserted into the buffer) implements the move assignment operator. However, I get a compiler error if I try to push instances something like this:

struct Foo {
    Foo(int a) : m_bar(a) {}
    int m_bar;
    Foo& operator=(Foo& other) {
        this.m_bar = other.m_bar;
    }
}

      

If I try to compile buffer.push(Foo(42));

, I get a compiler error for the method push(T&& item)

on the line that it reads *head = std::move(item);

. The error is that there is no viable overload for operator=

that takes an rvalue - that's right, there isn't. There is only an assignment operator that accepts lvalues.

But since I cannot make sure that every object that will ever be stored in my container will have the move assignment operator correctly implemented, I need to make sure that this case is handled correctly. What's more, std :: vector handles this without issue. When an object implements a redirect destination push_back

, it will move it if it doesn't copy it. Whether it is an rvalue or not. In fact, if I put my problematic Foo

rvalue that was previously throwing an error in the std :: vector, it works as it should.

So what am I missing? How can my container implement move semantics and still maintain rvalue references for objects that do not implement move purpose?

+3


source to share


1 answer


What you do / think is wrong is the incorrect signature of the copy assignment operator:

Foo& operator=(Foo& other);

      

which takes a non-const lvalue reference to an instance other

. This prevents backtracking back into a regular copy if the assignment operator assigned by the user does not receive a reference to the rvalue (i.e. the rvalue can be bound by a const lvalue reference ), so it should be:

Foo& operator=(const Foo& other);
//             ~~~~^

      

Then why is he working with std::vector<Foo>

, then?

The operator buffer.push_back(Foo(42));

uses the copy constructor, not the assignment operator. This works because it Foo

has an implicitly instantiated copy of the following signature:



Foo(const Foo&);

      

which works for both lvalues ​​and rvalues ​​( DEMO ).

What are you trying to do:

*head = std::move(item);

      

- use the assignment operator. Since you declared one yourself, the compiler cannot implicitly generate one that accepts an lvalue constant reference, nor can it use a user-declared member that accepts an lvalue constant reference, which results in the error you see.

Consider using allocators or the placement-new operator in your operations push

, using a parameter item

as an argument to the constructor Foo

instead of using the copy assignment and carry operators.

+4


source







All Articles