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.