Is a '=Default' Move Constructor Equivalent to a Member-Wise Move Constructor

Is a `=default` move constructor equivalent to a member-wise move constructor?

Yes both are the same.

But

struct Example { 
string a, b;

Example(Example&& mE) = default;
Example& operator=(Example&& mE) = default;
}

This version will permits you to skip the body definition.

However, you have to follow some rules when you declare explicitly-defaulted-functions :

8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]

A function definition of the form:

  attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;

is called an explicitly-defaulted definition. A function that is explicitly defaulted shall

  • be a special member function,

  • have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where T is the name of the member function’s class) as if it had been implicitly declared,

  • not have default arguments.

Why no default move-assignment/move-constructor?

The implicit generation of move constructors and assignment operators has been contentious and there have been major revisions in recent drafts of the C++ Standard, so currently available compilers will likely behave differently with respect to implicit generation.

For more about the history of the issue, see the 2010 WG21 papers list and search for "mov"

The current specification (N3225, from November) states (N3225 12.8/8):

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, and

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

There is similar language in 12.8/22 specifying when the move assignment operator is implicitly declared as defaulted. You can find the complete list of changes made to support the current specification of implicit move generation in N3203: Tightening the conditions for generating implicit moves
, which was based largely on one of the resolutions proposed by Bjarne Stroustrup's paper N3201: Moving right along.

Default move constructor and reference members

It's handled by the specification of class member access expressions. The key part is

Let x be either the parameter of the constructor or, for the move constructor, an xvalue referring to the parameter.

In other words, a defaulted move constructor for

struct X { int x, &y; };

does the equivalent of

X::X(X&& other) : x(std::move(other).x), y(std::move(other).y) {}

The important thing here is that the result of a class member access expression x.m, where m names a non-static data member, is always an lvalue if m has reference type, but an xvalue if x is an rvalue and m has non-reference type. (See [expr.ref]/4.) This ensures that lvalue reference members will be initialized with lvalues.

Default member variables in defaulted copy/move constructors

A constructor must and will always initialize all the class data members.

Default initializers will be used in constructors where that data member isn't explicitly initialized. The default copy constructors will initialize each data member with a copy of the corresponding data member from the copied object, so default initializers won't be used.

This leads to a case where default initializers will be used in a copy constructor if it's user provided and it lacks that member initializer (thanks to NathanOliver for pointing this out).

E.g.:

A(whatever param) {}            // default initializer used
A(whatever param) : a{true} {} // default initializer NOT used
A(const A&) = default; // default initializer NOT used
A(const A&) : a{other.a} {} // default initializer NOT used
A(const A&) {} // default initializer used

Explicitly defaulted copy/move assignment operators implicitly deleted because field has no copy/move operators. C++

This data member

 const string theSeparators; 

is defined with the qualifier const. So it can not be reassigned after its initialization in a constructor.

Thus the compiler defined the copy and move assignment operators as deleted.

You can just remove the qualifier const for this data member.

Or if to keep the qualifier const for the data member then you have to use mem-initialing lists in constructors like for example

Dictionary::Dictionary(const string& filename, const string& separators = "\t\n")
: filename( filename ), theSeparators( separators )
{
//...
}

But in this case the copy and move assignment operators will be still deleted.

Why does a move constructor require a default constructor for its members?

Use the constructor's initializer list to initialize the A member. As written, the move constructor uses, as the compiler says, the default constructor for A.

B(B&& b) : a(std::move(b.a)) {}

Default move constructor of classes with reference members

Default copy-constructor

Data(Data const&) = default;

is mostly equivalent to:

Data(Data const& rhs) :
value_(rhs.value_), // Copy value
value(rhs.value) // alias the same value as rhs.value, so probably rhs.value_
{
}

So you don't point on internal member as you expect.

Same for default move constructor with some std::move.

What is std::move(), and when should it be used?

Wikipedia Page on C++11 R-value references and move constructors

  1. In C++11, in addition to copy constructors, objects can have move constructors.

    (And in addition to copy assignment operators, they have move assignment operators.)
  2. The move constructor is used instead of the copy constructor, if the object has type "rvalue-reference" (Type &&).
  3. std::move() is a cast that produces an rvalue-reference to an object, to enable moving from it.

It's a new C++ way to avoid copies. For example, using a move constructor, a std::vector could just copy its internal pointer to data to the new object, leaving the moved object in an moved from state, therefore not copying all the data. This would be C++-valid.

Try googling for move semantics, rvalue, perfect forwarding.

Why does clang, using libstdc++, delete the explicitly defaulted constructor on a type containing std::optional?

This is a combination of:

  • an underspecification in the Standard;
  • a suboptimal Library implementation; and
  • a compiler bug.

Firstly, the Standard does not specify whether for the default optional.ctor a permissible implementation would be to defined it as defaulted:

constexpr optional() noexcept = default;
^^^^^^^^^ OK?

Note that functions.within.classes answers the question in the affirmative for copy/move constructors, assignment operators, and non-virtual destructors, but does not mention default constructors.

This matters because it affects program correctness; on the assumption that optional has data members approximating the following:

template<class T>
class optional {
alignas(T) byte buf[sizeof(T)]; // no NSDMI
bool engaged = false;
// ...
};

then since buf is a direct non-variant non-static data member lacking a default member initializer, if the default constructor of optional is defined as defaulted and thus is not user-provided, optional is not const-default-constructible and so optional<A> const a; is ill-formed.

It is thus a bad idea for a Library to define the default constructor of optional as defaulted, not only for this reason but also because it makes a value-initialized optional<B> b{}; perform more work than necessary, since it must zero-initialize buf, as observed std::optional - construct empty with {} or std::nullopt? - see in particular this answer. libstdc++ is fixed in this commit, which will be included in the next release of gcc, presumptively gcc 11.

Finally, it is a bug in gcc that it allows a non-const-default-constructible type for a const non-static data member without default member initializer of a class type whose default constructor is defined as defaulted; clang is correct to reject it. A reduced testcase is:

struct S {
S() = default;
int const i;
};

The best workaround in your case would be to supply a NSDMI:

const std::optional<std::string> m_value = std::nullopt;
^^^^^^^^^^^^^^

or (though I prefer the former, as it gives better codegen under Clang/libstdc++):

const std::optional<std::string> m_value = {};
^^^^

You might also consider giving Foo a user-defined default constructor; this results in better codegen under gcc (not zeroing the buffer but only setting the engaged member to false) thanks to what is presumably a related compiler bug.

What do explicitly-defaulted constructors do?

They aren't equivalent to any function bodies. There are small but significant differences between the three cases: = default, allowing implicit generation, and the nearest equivalent function body.

The following links explain in more detail:

  • Defaulted default constructor and destructor
  • Defaulted move constructor

I couldn't find a good link about copy-constructor; however similar considerations as mentioned in the other two links will apply.


myclass<int> x; does not set value to 0.

The defaulted move-constructor (if you made it a non-const reference) moves each movable member (although I think there is a special case where if there is a non-movable base class, weird things happen...)



Related Topics



Leave a reply



Submit