Ensuring forced forced generation of special member functions

In C ++ 11, you can explicitly specify a special member function if its implicit generation was automatically prevented.

However, explicitly prohibiting the execution of a special member function cancels the implicit deletion caused by the manual declaration of some other special member functions (copy operations, destructor, etc.), this does not force the compiler to generate the function and the code is considered well-formed, even if the function actually cannot be generated.

Consider the following scenario:

struct A { A () = default; A (const A&) = default; A (A&&) = delete; // Move constructor is deleted here }; struct B { B () = default; B (const B&) = default; B (B&&) = default; // Move constructor is defaulted here A a; }; 

The move constructor in B will not be generated by the compiler, because this will lead to a compilation error (the move constructor is deleted). Without explicitly deleting constructor B, the move constructor B will be generated as expected (copying A, not moving it).

Attempting to move such an object will silently use the copy constructor instead:

 B b; B b2 (std::move(b)); // Will call B copy constructor 

Is there a way to force the compiler to either generate a function or throw a compilation error if it cannot? Without this guarantee, it is difficult to rely on default move constructors if one remote designer can turn off move for entire object hierarchies.

+5
source share
1 answer

There is a way to detect types like A But only if the type explicitly removes the move constructor. If the move constructor is implicitly generated as remote, then it will not participate in overload resolution. This is why B is movable, although A not. B default a move constructor, which means that it is implicitly deleted, so copying occurs.

B therefore moves constructively. However, A not. So this is simple:

 struct B { static_assert(is_move_constructible<A>::value, "Oops..."); B () = default; B (const B&) = default; B (B&&) = default; // Move constructor is defaulted here A a; }; 

Now there is no general way to call any type that contains copy-only types to do what you want. That is, you must statically state each type separately; you cannot put some syntax in the default move constructor to try to move B

The reason for this is to partially support backward compatibility. Think of all the pre-C ++ 11 codec that custom copy constructors declared. By the rules for creating a move constructor in C ++ 11, all of them would remove the move constructors. This means that any form code T t = FuncReturningTByValue(); will fail, although in C ++ 98/03 it worked fine by calling the copy constructor. So the copy-by-copy problem worked around this, creating this copy instead of moving, if the move constructor could not be generated.

But since = default means "do what you usually do," it also includes this particular overload resolution behavior that ignores the implicitly remote move constructor.

+3
source

Source: https://habr.com/ru/post/1242301/


All Articles