initialization lists in copy constructor - c++

#include <iostream>
using namespace std;
struct A{
A(){}
A(const A&) {cout<<"1";}
};
struct B: A{
B(){}
B(const B&){}
};
struct C: A{
C(){}
C(const B&rhs):A(rhs){}
};
struct D:A{
D(){}
};
int main(){
D d;
D dcopy(d);
C c;
C ccopy(c);
B b;
B bcopy(b);
}
when I run this program I have expected output as: 111, but it is giving output as:11, please explain !!!
C(const B& rhs) : A(rhs) {} // presence of this line has no influence on output,
// please explain, why?

B(const B&){}
This code doesn't output anything, nor does it copy-construct the A (it default constructs it). So B bcopy(b) doesn't output anything. Perhaps you meant:
B(const B& rhs) : A(rhs) {}

According to the C++ Standard
8 In a non-delegating constructor, if a given non-static data member
or base class 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) and the entity is not a virtual base class of
an abstract class (10.4), then ...
— otherwise, the entity is default-initialized (8.5).
And
15 The implicitly-defined copy/move constructor for a non-union class
X performs a memberwise copy/move of its bases and members.
So in your example class D has implicitly defined copy constructor and according to the second quote it performs coping of its base class that is it calls the copy constructor of A.
The same is valid for class C (C has no explicitly user-defined copy constructor, so the compiler defines an implicit copy constructor. This definition C(const B& rhs) : A(rhs) {} is not a definition of the copy constructor. It is a constructor with one parameter).
Class B has no implicitly defined copy constructor. It has a user-defined copy constructor. So according to the first quote it performs default initialization of its subobjects that is it calls the default constructor of class A.

Related

Can the compiler generates a default copy constructor that takes reference to different class type?

I have this example
struct B { B(); };
struct D : B { };
D d{ B() }; // what happens here? or why it's well-formed
This is an aggregate initialization, but I can't understand how d is constructed? Does the compiler generates implicitly a copy constructor with this signature D::D(const D&) or D::D(const B&) or what? It's clear that the compiler does not generate D::D(const D&) because const D& = B() is ill-formed. So this means it generates a copy constructor D::D(const B&)?
Now what would happen if I inherits constructors from B:
struct B { B(); };
struct D : B { using B::B; };
D d{ B() }; // why it's ill-formed?
One said to me that the default copy constructor of B which is B::B(const B&) is inherited into D but it's excluded from the set of candidates, that's why it's ill-formed. Is that true?
Can the compiler generates a default copy constructor that takes reference to different class type?
By definition, no. A constructor that accepts an object of another type is not a copy constructor. It would be a converting constructor.
No such converting constructor is implicitly generated.
This is an aggregate initialization, but I can't understand how d is constructed?
No constructor of the enclosing class is called in aggregate initialisation. The sub objects are initialised directly.
D is an aggregate with a base class of type B. Aggregate initialisation is used to initialise this base sub object.
It's clear that the compiler does not generate D::D(const D&) because const D& = B() is ill-formed.
Former cannot be deduced from the latter. In fact, there is a (trivial) D::D(const D&) which you can prove by attempting copy initialisation:
D d1{};
D d2(d1); // works
That said, a trivial constructor is a concept for the abstract machine, and the compiler doesn't have to generate anything in practice.
Now what would happen if I inherits constructors from B
struct D : B { using B::B; };
D d{ B() }; // why it's ill-formed?
Having inherited constructors disqualifies the class from being an aggregate and hence aggregate initialisation does not apply. List initialisation will attempt to call a constructor, but no converting constructor exists.

Implicit move constructor

What is exactly the implicit move constructor doing? For example how would the implicit move constructor look like for the following class (could you provide some example implementation of this implicit constructor):
struct A
{
A() = default;
A(A && other) = default;
int a;
};
struct B : public A
{
int b;
int * c;
};
Would the implementation look like this:
B(B && other) : A(std::move(other)), b(std::move(other.b)), c(std::move(other.c)) {}
From cppreference.com:
For union types, the implicitly-defined move constructor copies the
object representation (as by std::memmove). For non-union class types
(class and struct), the move constructor performs full member-wise
move of the object's bases and non-static members, in their
initialization order, using direct initialization with an xvalue
argument. If this satisfies the requirements of a constexpr
constructor, the generated move constructor is constexpr.
The base class constructors runs before the derived one.

C++ Default constructor not inherited with "using" when move and copy constructors present

class A{
public:
A(){};
};
class B : public A{
public:
using A::A;
B(const B&) = default;
B( B&&) = default;
};
B b;
The compiler (g++ (5.4.0-6ubuntu1) / c++11) says "no matching function for call to B::B()" and lists the copy and move constructors as candidates. If I comment those defaulted ones out then it compiles. What causes this? And what difference does it make that they are explicitly defaulted? If those 2 lines weren't there they would be defaulted anyway.
Before C++17, the default constructor of the base class won't be inherited via using:
All candidate inherited constructors that aren't the default constructor or the copy/move constructor and whose signatures do not match user-defined constructors in the derived class, are implicitly declared in the derived class. (until C++17)
After C++17 the code works fine.
Before that, the default constructor won't be inherited from the base class, and won't be generated for class B because copy/move constructor are provided.
If no user-declared constructors of any kind are provided for a class type (struct, class, or union), the compiler will always declare a default constructor as an inline public member of its class.
That's why if you comment copy/move constructor out it compiles. You can add the definition explicitly as a pre-C++17 workaround. e.g.
class B : public A {
public:
B(const B&) = default;
B( B&&) = default;
B() = default;
};
The code compiles with gcc8.
If you declare any constructors, the default constructor is not implicitly generated, you can generate it by adding a = default for it as well:
class B : public A {
public:
B() = default;
B(const B&) = default;
B( B&&) = default;
};
This has changed with C++17 (as pointed out by other answer).
The default constructor cannot be inherited, the standard explicitly says so. Quoting C++11 12.9 [class.inhctor]/3 (emphasis mine) (*):
For each non-template constructor in the candidate set of inherited constructors other than a constructor
having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly
declared with the same constructor characteristics unless there is a user-declared constructor with the same
signature in the class where the using-declaration appears. ...
This means that for the default constructor, normal rules apply as if the using A::A; declaration wasn't there. So the presence of any other constructor declaration (such as the copy & move constructor) causes the default constructor not to be implicitly declared. Note that you can easily add it back by explicitly defaulting it:
class B : public A{
public:
using A::A;
B() = default;
B(const B&) = default;
B( B&&) = default;
};
(*) The same wording is present in C++14 (n4140), at the same location. I can't seem to find equivalent wording in C++1z (looking through n4582)

Value initialization for classes with exclusively inherited constructors

According to cppreference non-union class types without any user-provided constructors will be zero-initialized before being constructed:
If T is an non-union class type without any user-provided constructors, then the object is zero-initialized and then the implicitly-declared default constructor is called (unless it's trivial)
I'm not sure what should happen when the c++11 inherited constructors are used since the quote explicitly mentions the implicitly-declared default constructor.
Given the following example:
#include <iostream>
struct A {
int a;
A() {}
A(int i): a(i) {}
};
struct B: public A {
using A::A;
};
int main() {
B b { 5 };
B* p = new (&b) B{ };
std::cout << b.a << std::endl;
}
What is the correct output, 0 or 5? Should a class type exclusively providing inherited constructors be zero-initialized before value-initialization (B{ })?
The correct answer is 0 because the default constructor for B is implicitly declared.
Note that default, copy & move constructors are not inherited; quoting from §12.9/3 [class.inhctor]
For each non-template constructor in the candidate set of inherited constructors other than a constructor
having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly
declared with the same constructor characteristics unless there is a user-declared constructor with the same
signature in the complete class where the using-declaration appears or the constructor would be a default,
copy, or move constructor for that class.
Your example is similar to the one listed in N3797, §12.9/6 (edited for brevity)
struct B2 {
B2(int = 13, int = 42);
};
struct D2 : B2 {
using B2::B2;
};
The candidate set of inherited constructors in D2 for B2 is
— B2(const B2&)
— B2(B2&&)
— B2(int = 13, int = 42)
— B2(int = 13)
— B2()
The set of constructors present in D2 is
— D2(), implicitly-declared default constructor, not inherited
— D2(const D2&), implicitly-declared copy constructor, not inherited
— D2(D2&&), implicitly-declared move constructor, not inherited
— D2(int, int), implicitly-declared inheriting constructor
— D2(int), implicitly-declared inheriting constructor
In your case, the candidate set of inherited constructors in B for A are
A()
A(int)
A(const& A)
A(A&&)
and the constructors present in B are
B() implicitly declared, not inherited
B(int) implicitly declared, inherited
B(const& B) implicitly declared, not inherited
B(B&&) implicitly declared, not inherited

c++ is this copy constructor?

class A {};
class B { public: B (A a) {} };
A a;
B b=a;
I read this from http://www.cplusplus.com/doc/tutorial/typecasting/ . It says this is a implicit type conversion. From class A to class B.
I want to ask, is this also an example of copy constructor?
Thanks.
No, it's not a copy constructor. A copy constructor copies one object of one type into another of the same type:
B::B(const B& b)
{
// ...
}
As a side note, if you need a copy constructor then you also need a destructor and an assignment operator, and probably a swap function.
What B::B(A) is is a conversion function. It's a constructor that allows you to convert an object of type A into an object of type B.
void f(const B& obj);
void g()
{
A obja;
B objb = obja;
f(obja);
}
No, A copy constructor has the form
class A
{
public:
A(const A& in) {...}
}
No, a copy constructor is called when you create a new variable from an object. What you have there is two objects of different types.
The line B b = a; implies that a copy constructor is used, as if you had typed B b = B(a); or B b((B(a)));. That is, the compiler will check whether B has an accessible (public) copy constructor - whether user-defined or the default one provided by the compiler. It doesn't mean, though, that the copy constructor has to be actually called, because the language allows compilers to optimize away redundant calls to constructors.
By adding a user-defined copy constructor to B and making it inaccessible, the same code should produce a compiler error:
class A {};
class B {
public:
B (A ) {}
private:
B (const B&) {} // <- this is the copy constructor
};
A a;
B b=a;
For example, Comeau says:
"ComeauTest.c", line 10: error: "B::B(const B &)" (declared at line 6), required
for copy that was eliminated, is inaccessible
B b=a;
^