Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forwarding constructors calls 2 times copy constructor of base class

The following code forwards constructors from base to derived class.

Why 2 copy constructor calls? What happen in the background?

Compiled with g++.

#include <iostream>
using namespace std;

struct A {
    A() { cout << "A" << endl; }
    A(const A&) { cout << "A(const A&)" << endl; }
    template<typename T> A(T a); // Needed to compile :-O
};

template<typename T>
struct C : public T { using T::T; };

int main()
{
    A a;
    C<A> ca(a);
    //C<A> caa(ca);
    return 0;
}

Output is:

A
A(const A&)
A(const A&)
like image 721
Chameleon Avatar asked Dec 22 '13 22:12

Chameleon


People also ask

How many times is copy constructor called?

You call the function by value and do two copies inside.

Can a constructor call another constructor of the same class C++?

No, in C++ you cannot call a constructor from a constructor. What you can do, as warren pointed out, is: Overload the constructor, using different signatures. Use default values on arguments, to make a "simpler" version available.

Can a copy constructor have multiple arguments?

A copy constructor has as its first parameter a (possibly const or volatile) reference to its own class type. It can have more arguments, but the rest must have default values associated with them.

What are copy constructors in C++?

A copy constructor is a member function that initializes an object using another object of the same class. In simple terms, a constructor which creates an object by initializing it with an object of the same class, which has been created previously is known as a copy constructor.


2 Answers

By defining a constructor template in A, C will be given a constructor template with a similar signature. It is implicitly defined similarly to:

template<typename T>
struct C : public T
{
    //using T::T;

    C() = default;
    C(C const&) = default;

    template<typename U> C(U a) : T( std::forward<U>(a) ) {}
};

This now calls the copy-constructor of A twice: once for taking the argument by value. The second call results from T( std::forward<U>(a) ) calling the copy-ctor of A. This was surprising to me, as you'd expect an inherited ctor to call the exact ctor of the base class of which it has been inherited. But that's not the case, overload resolution selects not the ctor template of A, but the plain copy-ctor A(A const&) (see below).


Interestingly, it doesn't care much what the constructor template in A does, it only needs to be declared. That's why in the OP, the definition can be missing; it can also be deleted (which might be a defect?).

The copy-ctor of A only has to be chosen during overload resolution of the initialization T( std::forward<U>(a) ). This is the case here: The argument is an rvalue of type A, which can bind directly to a const A& reference, as required by the copy-ctor of A. As the reference binding is direct and w/o derived-to-base conversion, the copy-ctor ranks as an Exact Match. The ctor template in A is ranked as an Exact Match as well, but as there's a template- and non-template function with the same rank in the overload set, the non-template function is preferred (the copy-ctor A(A const&)).

like image 86
dyp Avatar answered Sep 24 '22 17:09

dyp


One call to A::A(const A&) is the base class constructor of C<A>.

The other is called to copy the pass-by-value parameter.

like image 34
Ben Voigt Avatar answered Sep 22 '22 17:09

Ben Voigt