Pergunta

Consider the following code.

#include <iostream>
#include <type_traits>

struct A
{
    int x;
    A() = default;
    ~A() = default;

    A(const A&) = delete;
    A &operator=(const A&) = delete;

    A(A &&) = default;
    A &operator=(A &&a) = default;
};

int main()
{
    std::cout << std::boolalpha;
    std::cout << std::is_copy_constructible<A>::value << std::endl;
    std::cout << std::is_copy_assignable<A>::value << std::endl;

    A a;
    a.x = 3;

    A b = std::move(a);

    std::cout << std::hex << a << " " << &b << std::endl;
    std::cout << a.x << " " << b.x;

    return 0;
}

The example input is:

false
false
0xbfd1ee28 0xbfd1ee2c
3 3

As you can see, the variable b has been successfully copied although the class A is noncopyable by design. I totally understand that the variable a won't be moved anywhere. The std::move call should probably construct an rvalue-reference copying the object a first, but hey, isn't it forbidden in the class definition?

Can anybody explain, what is going on in here?

Foi útil?

Solução

Why do you think there was a copy? You asked the compiler to generate a move constructor for you. The generated move constructor will do a memberwise move of all elements. There are basically two scenarios:

  1. The corresponding member has a move constructor in which case this move constructor will be applied.
  2. The corresponding member has no move constructor in which case the copy constructor will be applied.

Moving built-in types is like copying them, i.e., they don't do anything to source of the move unlike, e.g., std::unique_ptr<T> which will put a null-pointer into the source.

When you used

A b = std::move(a);

you give a the appearance of a temporary object which can be move constructed, using the generated move constructor for A. The rvalue-reference isn't constructed but the same object is simply cast. std::move(a) is equivalent to static_cast<A&&>(a).

Outras dicas

A b = a;

This wouldn't work because A is non-copyable. That is, because a is an lvalue expression, this will attempt to use the copy constructor which has been deleted, so it won't compile.

A b = std::move(a);

This will work because A is moveable. That is, because std::move(a) is an rvalue expression, this will attempt to use the move constructor which is defaulted, so it will compile.

Only in the first case is a copy attempted. The second does not involve a copy. Just because b ended up being constructed, doesn't mean it was copied from a. Instead, it has been moved from a.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top