Are Move Constructors Produced Automatically

Are move constructors produced automatically?

A move constructor for a class X is implicitly declared as defaulted exactly when

  • 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,
  • X does not have a user-declared destructor, and
  • the move constructor would not be implicitly defined as deleted.

So for example, if your class has a class type data member that does not have a move constructor, your class will not get a move constructor even if it doesn't have any copy/move constructor declared, because the implicitly declared move constructor would be defined as deleted (because of that data member).

c++ move constructor generated with default constructor

It is nothing related to the move constructor It is about the default constructor. Try this:

class Y
{
public:
Y(const Y&) {}
};

struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem;
};
hasY hy; // This will cause an error because there is no default constructor

Now if you add the default constructor: Y(){}, the error will go away.


As @M.M commented, The copy constructor will be called in a such case.

You may try this code:

class Y{
public:
Y(){std::cout << "Default constructor\n";}
Y(const Y&) {std::cout << "Copy constructor\n";}
};

struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem;
};

int main(){
hasY hy;
hasY h=std::move(hy);
}

It will print:

Default constructor

Copy constructor

If you want to make the class not moveable you should delete the move constructor by yourself Y(Y&&)=delete;

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).

Default move constructor vs. Default copy constructor vs. Default assignment operator

I believe backwards compatibility plays a big part here. If the user defines any of the "Rule of three" functions (copy ctor, copy assignment op, dtor), it can be assumed the class does some internal resource management. Implicitly defining a move constructor could suddenly make the class invalid when compiled under C++11.

Consider this example:

class Res
{
int *data;

public:
Res() : data(new int) {}

Res(const Res &arg) : data(new int(*arg.data)) {}

~Res() { delete data; }
};

Now if a default move constructor was generated for this class, its invocation would lead to a double deletion of data.

As for the move assignment operator preventing default move constructor definitions: if the move assignment operator does something other than default one, it would most likely be wrong to use the default move constructor. That's just the "Rule of three"/"Rule of five" in effect.

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)

Are move constructors/assignment operators generated for derived classes

As far as the derived class is concerned, the fact that an attribute or a base class has user-defined or implicit special functions does not matter. All that matter is their presence.

Compliant C++11 compilers should automatically provide the move constructors and assignment operators for the struct and class, if possible (which is clearly defined in the Standard), even though only classes with dynamically allocated buffers will really benefit from it (moving an int is just copying it).

Therefore, if your class embeds a std::string or a std::unique_ptr (for example), then its move constructor will call the embedded string or unique_ptr move constructor and it will be efficient... for free.

Simply changing the compilation mode should thus slightly improve performance.

When should compiler generate move constructor?

This part of C++11 is unfortunately in flux. And whatever the standard is going to say, VC11 couldn't possibly implement it yet. So for today, I don't believe you'll be able to count on generated move members.

However, this is a good question and I wanted to get a good answer out on it.

In general, the compiler should generate move members if you have no user-declared copy members nor destructor. = default and = delete counts as user-declared. If you declare one move member (e.g. move constructor), the other will not be implicitly generated.

Unfortunately C++11 goes on to say that sometimes the move members are implicitly deleted when declared with =default, and sometimes their generation depends upon whether the bases and members have move members or are trivially copyable. This is all way too complicated and sometimes gives surprising behavior. Here is the CWG issue tracking this bug:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1402

As I write this, the issue does not have the correct proposed resolution. I expect that to change in about a week. At the 2012 Fall C++ standards meeting in Portland, OR, an agreement was reached which basically says:

  1. The compiler will never implicitly delete move members.
  2. The implicit generation of the move members will always be the same thing as = default.
  3. Implicit generation will not depend upon the triviality of the bases or members, nor whether or not they may throw when moved.

In a nutshell, I expect the corrected wording of CWG 1402 to simply say:

In general, the compiler should generate move members if you have no
user-declared copy members nor destructor. = default and = delete
counts as user-declared. If you declare one move member (e.g. move
constructor), the other will not be implicitly generated.
And if you =default a move member, you'll get something that moves
each base and member.

(in proper standardize). I have not yet seen the wording that will say this. Jason Merrill is writing it up for us.

This will mean that sometimes the compiler will implicitly generate throwing move members. But we were going for simple rules that nevertheless did the right thing most of the time (few surprises).

Automatically generated move constructor with not movable members

My original answer was wrong, so I'm starting over.


In [class.copy], we have:

A defaulted copy/
move constructor for a class X is defined as deleted (8.4.3) if X has:

— [...]

— a potentially constructed subobject 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,

— [...]

That bullet point applies to B(B&& other) = default;, so that move constructor is defined as deleted. This would seem to break compilation with std::move(), but we also have (via resolution of defect 1402):

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 ]

Ignoring is the key. Thus, when we do:

B b1;
B b2(std::move(b1));

Even though the move constructor for B is deleted, this code is well-formed because the move constructor simply doesn't participate in overload resolution and the copy constructor is called instead. Thus, B is MoveConstructible - even if you cannot construct it via its move constructor.

How does the move constructor look like if I have a vector (or anything like it) member variable?

Whatever a std::vector needs to do in order to move correctly, will be handled by its own move constructor.

So, assuming you want to move the member, just use that directly:

Shoplist(Shoplist&& other)
: slist(std::move(other.slist))
{}

and

Shoplist& Shoplist::operator=(Shoplist&& other)
{
slist = std::move(other.slist);
return *this;
}

In this case, you could as AndyG points out, just use = default to have the compiler generate exactly the same move ctor and move assignment operator for you.

Note that explicitly destroying the original as you did is definitely absolutely wrong. The other member will be destroyed again when other goes out of scope.


Edit: I did say assuming you want to move the member, because in some cases you might not.

Generally you want to move data members like this if they're logically part of the class, and much cheaper to move than copy. While std::vector is definitely cheaper to move than to copy, if it holds some transient cache or temporary value that isn't logically part of the object's identity or value, you might reasonably choose to discard it.



Related Topics



Leave a reply



Submit