Home > database >  Implicit call of Class constructor from Initialisation of another class
Implicit call of Class constructor from Initialisation of another class

Time:12-29

The following code-snippet seemingly works:

#include <iostream>

class Filling{
public:
  const int num;
  Filling(int num_)
  : num(num_)
  { }
};
class Nougat{
public:
  Nougat(const Filling& filling)
  : reference(filling)
  { }
  const Filling& get_filling() const{ return reference; }
private:
  const Filling& reference;
};
class Wrapper{
public:
  Wrapper(const Nougat& nougat)
  : reference(nougat)
  { }
  const Nougat& get_nougat() const{ return reference; };
private:
  const Nougat& reference;
};

int main(){
  Filling filling(3);
  Wrapper wrap(filling); /*(0)*/
  std::cout << wrap.get_nougat().get_filling().num << std::endl;
}

Although the initialisation is improper (at /*(0)*/), because the Wrapper class should accept a Nougat class, but instead it is given a Filling class.

Since the constructor takes references, I believe the compiler is creating a temporary Nougat class from the constructor argument, and then passes that to initialise the Wrapper class.

Getting a reference from a temporary results in undefined behaviour.

Is this what is really happening?

If so, how can this code compile?

CodePudding user response:

Wrapper's constructor expects a reference to Nougat, but you are giving it a Filling. As with all function calls in C , in such a situation implicit conversion sequences for all arguments are considered.

An implicit conversion sequence could be for example a cast from a derived class reference to a base class reference, but since Nougat and Filling are not related in that way, this doesn't apply here.

Another allowed step in an implicit conversion sequence are user-defined conversions. This also includes so-called converting constructors of the target type, that is a constructor not marked explicit. This is the case for the constructor Nougat(const Filling& filling), which allows for conversion from Filling to Nougat.

So, this constructor may be called to construct a temporary object of Nougat type as an implicit conversion. The resulting temporary can bind to a const lvalue reference as well.

To avoid such unintentional conversions, you should mark constructors as explicit, at least if they take exactly one argument, except if you really intent for such implicit conversions to happen.


By the end of the construction of wrapper, the temporary object is destroyed and therefore the reference obtained in

wrap.get_nougat()

is dangling, and it's following use causes undefined behavior.

  • Related