Search code examples
c++constructorinstantiationimplicit-conversionobject-slicing

Question about constructor matching and implicit conversion


I am doing an old school exam in preparation for my current exam and in one of the questions I am given this code:

#include <iostream>
using namespace std;

struct A
{
  A() : m_a(17) { cout << "A_default" << endl; }
  int m_a;
};

struct B1 : A
{
  B1() : m_b(1) { cout << "B1_default" << endl; }
  B1(const A& a)
  {
    cout << "B1_constructor_with_a_parameter_A" << endl;
    m_b = a.m_a + 1;
  }
  int m_b;
};

struct B2 : A
{
  B2() : m_b(1) { cout << "B2_default" << endl; }
  B2(const B1& b1)
  {
    cout << "B2_constructor_with_a_parameter_B1" << endl;
    m_b = b1.m_b + 100;
  }
  int m_b;
};



int main() 
{
  A a1;
  B1 b1;
  B2 b2 = b1;
  A a2 = b2;
  B1 b3 = b1;
  B1 b4 = b2;
  return 0;
}

This code prints:

A_default
A_default
B1_default
A_default
B2_constructor_with_a_parameter_B1
A_default
B1_constructor_with_a_parameter_A

The task is to match each printed line with the corresponding code line in main. I have gotten all of them right except the last printed line B1_constructor_with_a_parameter_A. The answer sheet says that this printed line comes from the B1 b4 = b2; line in main() but there is no explanation as to why this happens.

So my question is simple. Why does B1 b4 = b2; print B1_constructor_with_a_parameter_A when it doesn't match any constructor pattern in struct B1? Is there some sort of weird object slicing or implicit conversion going on here? That is my best guess. I am a bit confused.

If object slicing or implicit conversion does occur. Why doesn't the line B1 b3 = b1; print anything? I see no difference between B1 b3 = b1; and B1 b4 = b2; except that b2 is of type B2 and b1 is of type B1. Shouldn't the same rules apply since they both inherit A? I am confused.


Solution

  • Why does B1 b4 = b2; print B1_constructor_with_a_parameter_A when it doesn't match any constructor pattern in struct B1?

    It does that because b2 is of type B2, which inherits from A, which makes it implicitly convertible to A.

    Why doesn't the line B1 b3 = b1; print anything?

    Because for this line copy-constructor(which is provided by the compiler) of B1 is invoked since both are of the same type. In the case of B1 = B2, the only matching constructor is B1(const A& a). So, it implicitly converts to type A.

    Such behaviour and code is terrible and that's exactly why we should make our constructors explicit. Compile the above code with the following change and you will realize that it doesn't implicitly convert anymore:

    explicit B1(const A& a) { cout << "B1_constructor_with_a_parameter_A" << endl; }