Why does member initilizer invoke extra constructor call when moved? - c++

When a member initializer is used in a class that has a move constructor the initilized member's constructor gets called when the enclosing class is moved. Why does this happen? Please provide references to the standard. I have a guess as to what's happening that I give with the example results below.
Also, in slightly different scenrio, why doesn't the member's constructor get called if the initialized member is a plain-old-data type?
Also, what are best practices concerning member initializers and move constructors?
#include <bits/stdc++.h>
using namespace std;
struct C {
void do_stuff(){cout<<"stuff";}
C(){cout<<"C ctor"<<endl;}
~C(){cout<<"C DTOR"<<endl;}
};
struct Foo {
ifdef MEMBER_INIT
Foo() {cout<<"Foo ctor"<<endl;};
#else
Foo() : ptr(new C) {cout<<"Foo ctor"<<endl;};
#endif
Foo(Foo &) = delete;
Foo & operator=(Foo &) = delete;
Foo & operator=(Foo &&) = delete;
Foo(Foo && rhs){cout<<"Foo MOVE ctor"<<endl; rhs.ptr.swap(this->ptr); }
~Foo(){cout << "Foo DTOR "; if(ptr) ptr->do_stuff(); cout<<endl; }
#ifdef MEMBER_INIT
unique_ptr<C> ptr = make_unique<C>();
#else
unique_ptr<C> ptr;
#endif
};
int main()
{
Foo f;
Foo f2(move(f));
}
RESULTS:
g++ -std=c++14 x.cc && ./a.out
C ctor
Foo ctor
Foo MOVE ctor
Foo DTOR stuff
C DTOR
Foo DTOR
g++ -DMEMBER_INIT -std=c++14 x.cc && ./a.out
C ctor
Foo ctor
C ctor
Foo MOVE ctor
Foo DTOR stuff
C DTOR
Foo DTOR stuff
C DTOR
Why does using the member initializer invoke another constructor call for C?
Why does using the member initializer cause the Foo destructor to run C->do_stuff()?
My quess is that member initializers get evaluated for ALL constructor types before the actual constructor (in this case a move constructor) gets run. Is that correct?
I would specifically like references in the standard that verify or contradict my guess.

When MEMBER_INIT is defined move constructor performs ptr initialization using in-class initializer and becomes
Foo(Foo && rhs): ptr{make_unique<C>()}
Otherwise it is default-initialized.
15.6.2 Initializing bases and members [class.base.init]
9 In a non-delegating constructor, if a given potentially constructed subobject is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
…
the entity is initialized from its default member initializer as specified in 11.6;
Basically you forgot to actually manually initialize ptr field by moving:
Foo(Foo && rhs): ptr{::std::move(rhs.ptr)}

Related

C++ Why was the copy constructor called?

class A {
public:
A() {}
A(const A& a) { cout << "A::A(A&)" << endl; }
};
class B {
public:
explicit B(A aa) {}
};
int main() {
A a;
B b(a);
return 0;
}
Why does it print "A::A(A&)"?
When was the copy constructor for "A" called? And if the code calls the copy constructor, why can I remove the copy constructor without creating a compilation error?
B(A aa) takes an A by value, so when you execute B b(a) the compiler calls the copy constructor A(const A& a) to generate the instance of A named aa in the explicit constructor for B.
The reason you can remove the copy constructor and have this still work is that the compiler will generate a copy constructor for you in cases where you have not also declared a move constructor.
Note: The compiler generated copy constructor is often not sufficient for complex classes, it performs a simple member wise copy, so for complex elements or dynamically allocated memory you should declare your own.
§ 15.8.1
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 (11.4). The latter case is deprecated if
the class has a user-declared copy assignment operator or a user-declared destructor or assignment operator.
Why the copy happens
Look at your class B c'tor:
class B {
public:
explicit B(A aa) {}
};
You receive A by value, triggering a copy during the call.
If you would have change it to (notice A & aa):
class B {
public:
explicit B(A & aa) {}
};
There wouldn't be any copy...
Default copy constructor
When you remove the c'tor, the compiler generates one for you when it can trivially do so:
First, you should understand that if you do not declare a copy
constructor, the compiler gives you one implicitly. The implicit
copy constructor does a member-wise copy of the source object.
The default c'tor is equivalent to:
MyClass::MyClass( const MyClass& other ) :
x( other.x ), c( other.c ), s( other.s ) {}

Why must thrown objects be copy-initialized?

Exceptions use the statical type of an object to copy-initialize the thrown object. For instance:
struct foo
{
foo() = default;
foo(const foo&) = delete;
};
int main()
{
throw foo();
}
Clang++ --std=c++14 complains that the explicitly-deleted copy constructor can't be used. Why can't it be move-initialized instead?
It can't be move constructed because the type has no move constructor. A deleted copy constructor suppresses the implicit move constructor.
Modify the code to the following:
struct foo
{
foo() = default;
foo(const foo&) = delete;
foo(foo&&) = default;
};
int main()
{
throw foo();
}
Read this, the section "Implicitly-declared move constructor".
Because foo(foo&& ); is missing. By deleteing the copy constructor you've supressed move constructor as well.
The applicable phrasing from the standard (§[class.copy]/9) looks roughly like this (well, exactly like this, as of N4296):
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 ctor,
[...]
This applies because defining the copy ctor as deleted still means you've declared the copy ctor.

The case when the copy-constructor implicitly defined as deleted

The section N3797::12.8/11 [class.copy] says:
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 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
The first case about the ambiguity of corresponding copy/move constructor is quite clear. We can write the following:
#include <iostream>
using namespace std;
struct A
{
A(){ }
A(volatile A&){ }
A(const A&, int a = 6){ }
};
struct U
{
U(){ };
A a;
};
U u;
U t = u;
int main(){ }
to reflect that. But what about or a function that is deleted or inaccessible from the defaulted constructor? What's that got with a function inaccessible from the default constructor? Could you provide an example reflecting that?
Simply put:
struct M { M(M const&) =delete; };
struct X { X(X const&) =default; M m; }; // X(X const&) is actually deleted!
Implicitly-declared functions are also considered "defaulted" ([dcl.fct.def.default] / 5); a more familiar pre-C++11 example might be something like:
struct M { protected: M(M const&); };
struct X { M m; }; // X's implicit copy constructor is deleted!
Note that if you explicitly default the function after it has been declared, the program is ill-formed if the function would be implicitly deleted ([dcl.fct.def.default] / 5):
struct M { M(M const&) =delete; };
struct X { X(X const&); M m; };
X::X(X const&) =default; // Not allowed.
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
The wording is perhaps slightly contrived, for conciseness most certainly. The idea, as emphasised in the above, is that the function in question is the M copy constructor being overloaded in way which renders it inaccessible. So having a member of class M whose copy constructor is made protected for instance would delete the copy constructor of X. Likewise, simply deleting the copy constructor of M would have the same result.

How is a default move constructor for a class with STL members defined? [duplicate]

Is this
struct Example {
string a, b;
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
Example& operator=(Example&& mE) { a = move(mE.a); b = move(mE.b); return *this; }
}
equivalent to this
struct Example {
string a, b;
Example(Example&& mE) = default;
Example& operator=(Example&& mE) = default;
}
?
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.
Is a =default move constructor equivalent to a member-wise move constructor?
Yes. Update: Well, not always. Look at this example:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) = default;
nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "copy" << std::endl; }
movable( movable &&) { std::cerr << "move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c)); // prints copy
}
It prints:
copy
http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb
You declared defaulted move constructor, but copying happens instead of moving. Why? Because if a class has even a single non-movable member then the explicitly defaulted move constructor is implicitly deleted (such a pun). So when you run has_nonmovable d = std::move(c), the copy constructor is actually called, because the move constructor of has_nonmovable is deleted (implicitly), it just doesn't exists (even though you explicitly declared the move constructor by expression has_nonmovable(has_nonmovable &&) = default).
But if the move constructor of non_movable was not declared at all, the move constructor would be used for movable (and for every member that has the move constructor) and the copy constructor would be used for nonmovable (and for every member that does not define the move constructor). See the example:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) { std::cerr << "nonmovable::copy" << std::endl; }
//nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "movable::copy" << std::endl; }
movable( movable &&) { std::cerr << "movable::move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c));
}
It prints:
movable::move
nonmovable::copy
http://coliru.stacked-crooked.com/a/420cc6c80ddac407
Update: But if you comment out the line has_nonmovable(has_nonmovable &&) = default;, then copy will be used for both members: http://coliru.stacked-crooked.com/a/171fd0ce335327cd - prints:
movable::copy
nonmovable::copy
So probably putting =default everywhere still makes sense. It doesn't mean that your move expressions will always move, but it makes chances of this higher.
One more update: But if comment out the line has_nonmovable(const has_nonmovable &) = default; either, then the result will be:
movable::move
nonmovable::copy
So if you want to know what happens in your program, just do everything by yourself :sigh:
Yes, a defaulted move constructor will perform a member-wise move of its base and members, so:
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
is equivalent to:
Example(Example&& mE) = default;
we can see this by going to the draft C++11 standard section 12.8 Copying and moving class objects paragraph 13 which says (emphasis mine going forward):
A copy/move constructor that is defaulted and not defined as deleted
is implicitly defined if it is odrused (3.2) or when it is explicitly
defaulted after its first declaration. [ Note: The copy/move
constructor is implicitly defined even if the implementation elided
its odr-use (3.2, 12.2). —end note ][...]
and paragraph 15 which says:
The implicitly-defined copy/move constructor for a non-union class X
performs a memberwise copy/move of its bases and members. [ Note:
brace-or-equal-initializers of non-static data members are ignored.
See also the example in 12.6.2. —end note ] The order of
initialization is the same as the order of initialization of bases and
members in a user-defined constructor (see 12.6.2). Let x be either
the parameter of the constructor or, for the move constructor, an
xvalue referring to the parameter. Each base or non-static data member
is copied/moved in the manner appropriate to its type:
if the member is an array, each element is direct-initialized with the corresponding subobject of x;
if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
otherwise, the base or member is direct-initialized with the corresponding base or member of x.
Virtual base class subobjects shall be initialized only once by the
implicitly-defined copy/move constructor (see 12.6.2).
apart very pathological cases ... YES.
To be more precise, you have also to considered eventual bases Example may have, with exact same rules. First the bases -in declaration order- then the members, always in declaration order.

How Many default methods does a class have?

Sorry, this might seem simple, but somebody asked me this, and I don't know for certain.
An empty C++ class comes with what functions?
Constructor,
Copy Constructor,
Assignment,
Destructor?
Is that it? Or are there more?
In C++03 there are 4:
Default constructor: Declared only if
no user-defined constructor is
declared. Defined when used
Copy constructor - declared only if the user hasn't declared one. Defined if used
Copy-assignment operator same as above
Destructor same as above
In C++11 there are two more:
Move constructor
Move-assignment operator
It is also possible that the compiler won't be able to generate some of them. For example, if the class contains, for example, a reference (or anything else that cannot be copy-assigned), then the compiler won't be able to generate a copy-assignment operator for you. For more information read this
If I define the following class
class X
{};
The compiler will define the following methods:
X::X() {} // Default constructor. It takes zero arguments (hence default).
X::~X() {} // Destructor
X::X(X const& rhs) {}; // Copy constructor
X& operator=(X const& rhs)
{return *this;} // Assignment operator.
// Since C++11 we also define move operations
X::X(X&& rhs) {}; // Move Constructor
X& operator=(X&& rhs)
{return *this;} // Move Assignment
Note:
The default constructor is not built if ANY constructor is defined.
The other methods are not built if the user defines an alternative.
What is slightly more interesting is the default implementation when we have members and a base:
class Y: public X
{
int a; // POD data
int* b; // POD (that also happens to be a pointer)
Z z; // A class
};
// Note: There are two variants of the default constructor.
// Both are used depending on context when the compiler defined version
// of the default constructor is used.
//
// One does `default initialization`
// One does `zero initialization`
// Objects are zero initialized when
// They are 'static storage duration'
// **OR** You use the braces when using the default constructor
Y::Y() // Zero initializer
: X() // Zero initializer
, a(0)
, b(0)
, z() // Zero initializer of Z called.
{}
// Objects are default initialized when
// They are 'automatic storage duration'
// **AND** don't use the braces when using the default constructor
Y::Y()
:X // Not legal syntax trying to portray default initialization of X (base class)
//,a // POD: uninitialized.
//,b // POD: uninitialized.
,z // Not legal syntax trying to portray default initialization of z (member)
{}
//
// Note: It is actually hard to correctly zero initialize a 'automatic storage duration'
// variable (because of the parsing problems it tends to end up a a function
// declaration). Thus in a function context member variables can have indeterminate
// values because of default initialization. Thus it is always good practice to
// to initialize all members of your class during construction (preferably in the
// initialization list).
//
// Note: This was defined this way so that the C++ is backward compatible with C.
// And obeys the rule of don't do more than you need too (because we want the C++
// code to be as fast and efficient as possible.
Y::Y(Y const& rhs)
:X(rhs) // Copy construct the base
,a(rhs.a) // Copy construct each member using the copy constructor.
,b(rhs.b) // NOTE: The order is explicitly defined
,z(rhs.z) // as the order of declaration in the class.
{}
Y& operator=(Y const& rhs)
{
X::operator=(rhs); // Use base copy assignment operator
a = rhs.a; // Use the copy assignment operator on each member.
b = rhs.b; // NOTE: The order is explicitly defined
z = rhs.z; // as the order of declaration in the class.
return(*this);
}
Y::~Y()
{
Your Code first
}
// Not legal code. Trying to show what happens.
: ~z()
, ~b() // Does nothing for pointers.
, ~a() // Does nothing for POD types
, ~X() ; // Base class destructed last.
// Move semantics:
Y::Y(Y&& rhs)
:X(std::move(rhs)) // Move construct the base
,a(std::move(rhs.a)) // Move construct each member using the copy constructor.
,b(std::move(rhs.b)) // NOTE: The order is explicitly defined
,z(std::move(rhs.z)) // as the order of declaration in the class.
{}
Y& operator=(Y&& rhs)
{
X::operator=(std::move(rhs)); // Use base move assignment operator
a = std::move(rhs.a); // Use the move assignment operator on each member.
b = std::move(rhs.b); // NOTE: The order is explicitly defined
z = std::move(rhs.z); // as the order of declaration in the class.
return(*this);
}
Just to expand on Armen Tsirunyan answer here are the signatures for the methods:
// C++03
MyClass(); // Default constructor
MyClass(const MyClass& other); // Copy constructor
MyClass& operator=(const MyClass& other); // Copy assignment operator
~MyClass(); // Destructor
// C++11 adds two more
MyClass(MyClass&& other) noexcept; // Move constructor
MyClass& operator=(MyClass&& other) noexcept; // Move assignment operator
Default methods assigned by compiler for a empty class:
http://cplusplusinterviews.blogspot.sg/2015/04/compiler-default-methods.html
Is that it?
Yes thats it.
Compiler generates by default
A default constructor
A copy constructor
A copy assignment operator
A destructor
for a class
You can see the default constructor, the copy constructor and the assignment operator being generated by default when you use -ast-dump option of Clang
prasoon#prasoon-desktop ~ $ cat empty.cpp && clang++ -cc1 -ast-dump empty.cpp
class empty
{};
int main()
{
empty e;
empty e2 = e;
{
empty e3;
e3 = e;
}
}
typedef char *__builtin_va_list;
class empty {
class empty;
inline empty() throw(); //default c-tor
//copy c-tor
inline empty(empty const &) throw() (CompoundStmt 0xa0b1050 <empty.cpp:1:7>)
//assignment operator
inline empty &operator=(empty const &) throw() (CompoundStmt 0xa0b1590 <empty.cpp:1:7>
(ReturnStmt 0xa0b1578 <col:7>
(UnaryOperator 0xa0b1558 <col:7> 'class empty' prefix '*'
(CXXThisExpr 0xa0b1538 <col:7> 'class empty *' this))))
};