Why two variants of every constructor are created by the compiler?

Why the clang++ generates two constructors in the binary? One with “C1” in its name, the other with “C2” instead?
Compiling the code m1.cpp

struct X
{
  X();
  X(int);
};
X::X() {}
X::X(int) {}

with c++ -c m1.cpp leads to an object file m1.o containing both constructors twice.
The output of nm m.o shows:

0000000000000040 T __ZN1XC1Ei
0000000000000010 T __ZN1XC1Ev
0000000000000030 T __ZN1XC2Ei
0000000000000000 T __ZN1XC2Ev

Why there are two variants for one given constructor in the binary?

I’m currently using Apple clang version 14.0.3 (clang-1403.0.22.14.1). But I saw this behaviour with other versions of clang and g++ too.

<ctor-dtor-name> ::= C1                        # complete object constructor
                 ::= C2                        # base object constructor
                 ::= C3                        # complete object allocating constructor
                 ::= CI1 <base class type>     # complete object inheriting constructor
                 ::= CI2 <base class type>     # base object inheriting constructor
                 ::= D0                        # deleting destructor
                 ::= D1                        # complete object destructor
                 ::= D2                        # base object destructor

Itanium C++ ABI

1 Like

That is, C1 gets used for X x; whilst struct Y : X {}; Y y; uses C2.

1 Like

The Itanium ABI for C++ specifies three different constructor types, though the third type isn’t as commonly supported (clang doesn’t use it, for example).

The constructor that ends in C1 are complete object constructors; the constructor that ends in C2 is a base object constructor. What’s the difference between these two types? Well, if you look at the IR of the code, you’ll notice that the C1 variant is defined as follows: @_ZN1XC1Ev = dso_local unnamed_addr alias void (ptr), ptr @_ZN1XC2Ev (that is to say, the two functions are literally pointing to the same function body). But if you have a class with a virtual base class, now the two function bodies are different–the C1 constructor will call the C2 constructor for the virtual base class, but the C2 constructor omits calls to virtual base class constructors.

In short, the C2 constructor is required to call all the C2 constructors of its nonvirtual base classes, while the C1 constructor is required call all the C2 constructors of its virtual and nonvirtual base classes. If there are no virtual base classes in play, then there is no difference between the C1 and the C2 constructor.

1 Like