What Are the Rules For Automatic Generation of Move Operations

What are the rules for automatic generation of move operations?

From the standard Ch. 12 - Special member functions

Par 12.8 Copying and moving class objects (emphasis mine)

9 . If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

— X does not have a user-declared copy constructor,

— X does not have a user-declared copy assignment operator,

— X does not have a user-declared move assignment operator, and

— X does not have a user-declared destructor.

[ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that
otherwise would have invoked the move constructor may instead invoke a copy constructor. —end note ]

Then 11 explains the rules for deleting the defaulted move constructor

11 . An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/ move constructor for a class X is defined as deleted (8.4.3) if X has:

— a variant member with a non-trivial corresponding constructor and X is a union-like class,

— a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,

— a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,

— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor, or,

— for the copy constructor, a non-static data member of rvalue reference type. A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4).

[ Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. —end note ]



On the complexity of it all *

The rules can be somewhat overwhelming. It's good to use some technique to bypass the complexity. Examples are :

  1. Make use of the rule of zero to simplify the writing of the majority of your classes.
  2. (On implicitly deleted) Explicitly default the special member function in question; if it would have been implicitly defined as deleted, the compiler will complain.

* points made in the comments by myself (1) and dyp (2)

Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?

In the following, "auto-generated" means "implicitly declared as defaulted, but not defined as deleted". There are situations where the special member functions are declared, but defined as deleted.

  • The default constructor is auto-generated if there is no user-declared constructor (§12.1/5).
  • The copy constructor is auto-generated if there is no user-declared move constructor or move assignment operator (because there are no move constructors or move assignment operators in C++03, this simplifies to "always" in C++03) (§12.8/8).
  • The copy assignment operator is auto-generated if there is no user-declared move constructor or move assignment operator (§12.8/19).
  • The destructor is auto-generated if there is no user-declared destructor (§12.4/4).

C++11 and later only:

  • The move constructor is auto-generated if there is no user-declared copy constructor, copy assignment operator or destructor, and if the generated move constructor is valid (§12.8/10).
  • The move assignment operator is auto-generated if there is no user-declared copy constructor, copy assignment operator or destructor, and if the generated move assignment operator is valid (e.g. if it wouldn't need to assign constant members) (§12.8/21).

C++ declare a move/copy operation will suppress generation of related operations?

I define a move assignment, there should be not default copy assignment generated as said in the book

Correct.

but how could a3 gets the copy of a1?

It couldn't according to the standard. If the compiler does not give you a diagnostic message for this, then the compiler doesn't conform to the standard.


The result of a1+a2 is a rvalue

Correct.

how could this rvalue be passed to copy assignment whose argument is const A&?

Because rvalues can be bound to lvalue references to const. The lifetime of the temporary object is extended to match the potential lifetime of the reference. That is for the duration of the function in the case of a reference argument.

Has it been established if move/copy constructor/assignment are deleted or not declared in C++17?

According to N4659 (almost C++17 standard), they are still defined as defaulted but the behavior is (still) deprecated.

  • For the copy constructor, see [class.copy.ctor]/6:

If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted. The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.

  • For the copy assignment, see [class.copy.assign]/2:

If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted. The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor.

What does it means user-declared for Implicitly-declared move assignment operator?

Note that it says "user-declared copy assignment operators" (emphasis mine). Not every assignment operator is a copy assignment operator.

For class X, a copy assignment operator is defined as operator= which takes a parameter of type X, X&, const X&, volatile X&, or const volatile X&. So your Foo::operator=(Bar&&) is not a copy assignment operator, and thus does not affect the implicit declaration of a move (or copy) assignment operator.

Automatic generation of the move constructor

For me it doesn't print anything at all because the copy/move has been elided. However if I thwart RVO with something like:

extern bool choice;

B func() {
B b1, b2;
if (choice)
return b1;
return b2;
}

Then it prints:

move

It may be that your compiler does not yet implement the automatic generation of the move members.

Compiler not generating move constructors

Firstly, obj1 = std::move(obj2); invokes assignment operator, so it has nothing to do with constructors.

Yes, The compiler generates a move assignment operator for A, which perform member-wise move operation, including data member str. The problem is that after move operation str is left in valid, but unspecified state. Also see std::basic_string::operator=.

Replaces the contents with those of str using move semantics. str is in a valid but unspecified state afterwards.

I think you might observe the same result with only std::string, e.g.

std::string str1 = "Init string";
std::string str2 = "Obj2 string";
str1 = std::move(str2);
std::cout << str2;

LIVE with clang, just for reference; it gives the result as you expected but still remember the result is unspecified.



Related Topics



Leave a reply



Submit