Construction and initialization list : what the compiler do? - c++

I have some questions about constructors in C++. For each question (from (1) to (4)) I would like to know if the behaviour is perfectly defined regarding to the standard.
A) The first one is about initialization of members :
class Class
{
public:
Class()
: _x(0)
, _y(_x)
, _z(_y) // <- (1) Can I initialize a member with other members ?
{
;
}
protected:
int _x;
int _y;
int _z;
};
B) What are the functions added to each class by the compiler ?
template<typename T> class Class
{
public:
template<typename T0>
Class(const Class<T0>& other)
{
std::cout<<"My copy constructor"<<std::endl;
_x = other._x;
}
template<typename T0 = T>
Class (const T0 var = 0)
{
std::cout<<"My normal constructor"<<std::endl;
_x = var;
}
public:
T _x;
};
// (2) Are
// Class(const Class<T>& other)
// AND
// inline Class<T>& operator=(const Class<T>& other)
// the only functions automatically added by the compiler ?
As an example, if I call :
Class<int> a;
Class<int> b(a); // <- Will not write "My copy constructor"
Class<double> c(a); // <- Will write "My copy constructor"
(3) Is this behaviour perfectly normal according to the standard ?
(4) Do I have the guarantee that an empty constructor is not automatically added and that Class<int> x; will write "My normal constructor" ?

Can I initialize a member with other members ?
Yes, as long as those other members have already been initialised; i.e. as long as their declarations come before the member being initialised.
Are [the copy constructor] and [the copy-assignment operator] the only functions automatically added by the compiler ?
It will also implicitly declare a destructor, which will destroy _x using its destructor.
In C++11, a move constructor (Class(Class&&)) and move-assignment operator (Class& operator=(Class&&)) are also implicitly declared, unless you declare a copy or move constructor, or a copy or move assignment operator.
Note that your constructor template is not a copy constructor, and the implicit one will be used instead:
Class<T1> t1;
Class<T1>(t1); // prints nothing
Class<T2>(t1); // prints "My copy constructor" (which is a lie)
Is this behaviour perfectly normal according to the standard ?
Yes, see chapter 12.
Do I have the guarantee that an empty constructor is not automatically added and that Class<int> x; will write "My normal constructor" ?
Yes, a default constructor will only be implicitly declared if you don't declare any constructors at all.

Related

How can I inspect the default copy/move constructors/assignment operators?

How do I find out what exactly my classes' default constructors, destructors, and copy/move constructors/assignment operators do?
I know about the rule of 0/3/5, and am wondering what the compiler is doing for me.
If it matters, I'm interested in >=C++17.
The implicitly-defined copy constructor
... performs full member-wise copy of the object's bases and non-static members, in their initialization order, using direct initialization...
For a simple structure:
struct A
{
int x;
std::string y;
double z;
};
The copy-constructor would be equivalent to:
A::A(A const& otherA)
: x(otherA.x), y(otherA.y), z(otherA.z)
{
}
It just calls the respective operation for every member and direct base class, nothing else.
E.g. the implicitly generated copy assignment calls copy assignment for every member.
Note that:
virtual bases are always initialized by the most-derived class. Any initializers for the virtual bases in the member-init-lists of any bases are ignored. Example:
struct A
{
int x;
A(int x) : x(x) {}
};
struct B : virtual A
{
B()
: A(1) // This is ignored when constructing C.
{}
};
struct C : B
{
C()
: A(2) // If A is not default-constructible, removing this causes an error.
{}
};
C c; // .x == 2
The implicitly generated default constructor has a unique property (shared by a default constructor that's explicitly =defaulted in the class body): if the object was created using empty parentheses/braces, any field that would otherwise be uninitialized is zeroed. Example:
struct A
{
int x;
// A() = default; // Has the same effect.
};
A f; // Uninitialized.
A g{}; // Zeroed.
A h = A{}; // Zeroed.
A i = A(); // Zeroed.
This applies to scalar types, and the effect propagates recursively through member class instances that have the same kind of default constructors.

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 ) {}

Is default move constructor optimized away by compiler?

I have an std::multimap of the following type:
typedef std::multimap<std::pair<bool,uint32_t>, FooObject>
The FooObject has default move copy and assign constructors declared:
FooObject(FooObject&& v) = default;
FooObject& operator=(FooObject&& other)=default;
The copy/assign constructors are private to disable implicit copy.
So I should be able to emplace a pair into the map like this:
mymap.emplace(std::make_pair(false,32),FooObject());
This throws a list of errors with the one at the end:
error C2660: 'std::pair::pair': function does not take
2 arguments
If I declare move copy assign constructors without "default"
then it compiles ok.
FooObject(FooObject&& v){}
FooObject& operator=(FooObject&& other){}
Why is that? Does the compiler optimize away these constructors when marked with "default" keyword? I am using MSVC140
UPDATE:
Based on the comments below I found the reason - FooObject has a non-copiable member instance.
Here is the FooObject:
#define NO_COPY_ASSIGN(TypeName) \
TypeName (const TypeName &); \
void operator= (const TypeName &);
class FooObject
{
private:
NO_COPY_ASSIGN(FooObject)
public:
struct FooStruct
{
FooBuffer frameBuffer; //<--Here it is
}fooStruct;
FooObject(){}
/** Move constructor to allow insert into vector without copy */
FooObject(FooObject&& v) = default;
FooObject& operator=(FooObject&& other) = default;
};
*FooBuffer has also its copy/assign private.But I still don't get why replacing 'default' with {} fixes that.Please explain.
Your problem is that one of the member of FooObject is not move-able, which prevents the compiler from generating default move operations.
The {} versions of the move operations you implement yourself do no work (specifically: they don't actually do a move operation) on the members of FooObject and are thus legal.
The difference between
FooObject(FooObject&& v) = default;
and
FooObject(FooObject&& v){}
Is that the former will emit a constructor that moves each member from v while the latter default constructs each member and does nothing with v.
Since FooBuffer is not movable that means that the compiler will delete FooObject(FooObject&& v) = default; as it would be ill-formed.
With FooObject(FooObject&& v){} you do not have that problem as you never try to move the members of v. Since there is no member initialization list the compiler will add one for you that just default constructs the members.
You can see this behavior more explicitly with this:
struct Moveable
{
Moveable() = default;
Moveable(Moveable&&) { std::cout << "in Moveable(Moveable&&)\n"; }
};
struct Foo
{
Foo() = default;
Foo(Foo&&) = default;
Moveable m;
};
struct Bar
{
Bar() = default;
Bar(Bar&&){}
Moveable m;
};
int main()
{
Foo f;
Bar b;
std::cout << "test_f\n";
Foo test_f(std::move(f));
std::cout << "test_b\n";
Bar test_b(std::move(b));
}
which outputs
test_f
in Moveable(Moveable&&)
test_b
Live Example
Showing that nothing is actually moved in Bar's move constructor.

C++11 : Why is the copy ctor being called here?

Consider the code below running C++11. If I understand move semantics correctly, the copy constructor should not be called. But it is. Can someone explain why?
template<class D>
struct traced
{
public:
traced() = default;
traced(traced const&) { std::cout << typeid(D).name() << " copy ctor\n"; }
protected:
~traced() = default;
};
class A : public traced<A>{
public:
A(int x) : x_(x) {}
private:
int x_;
};
int main() {
// I thought the following two are equivalent. Apparently not.
aList.push_back(A(6)); // Prints out ".. copy ctor" ..
aList.emplace_back(6); // Does not print out " ... copy ctor"
}
aList.push_back(A(6));
This constructs a temporary A and moves it into the container. The implicitly generated move constructor of A is called, which needs to construct the base traced<A> from the base subobject of the temporary. However, trace explicitly declares a copy constructor, so it doesn't have move constructor by default, and A's move constructor nonetheless needs to perform a copy for its base class subobject.
aList.emplace_back(6);
This constructs an A directly into the container. No copy or move of any sort is involved.

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))))
};