从另一个 class 的初始化隐式调用 Class 构造函数

Implicit call of Class constructor from Initialisation of another class

以下代码片段似乎有效:

#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;
}

虽然初始化不正确(在/*(0)*/),因为Wrapperclass应该接受一个Nougatclass,但是却给出了一个 Filling class.

由于构造函数采用引用,我相信编译器正在从构造函数参数创建一个临时的 Nougat class,然后将其传递给初始化 Wrapper class.

从临时对象获取引用会导致未定义的行为。

这是真的吗?

如果是这样,这段代码如何编译?

Wrapper 的构造函数需要对 Nougat 的引用,但您给它的是 Filling。与 C++ 中的所有函数调用一样,在这种情况下 隐式转换序列 将考虑所有参数。

隐式转换序列可以是例如从派生的 class 引用到基础 class 引用的转换,但由于 NougatFilling 不相关这样,这不适用于此处。

隐式转换序列中另一个允许的步骤是用户定义的转换。这也包括目标类型的所谓 转换构造函数 ,即未标记 explicit 的构造函数。构造函数 Nougat(const Filling& filling) 就是这种情况,它允许从 Filling 转换为 Nougat

因此,可以调用此构造函数构造一个Nougat类型的临时对象作为隐式转换。生成的临时文件也可以绑定到 const 左值引用。

为避免此类无意的转换,您应该将构造函数标记为 explicit,至少如果它们只接受一个参数,除非您确实有意进行此类隐式转换。


wrapper 的构造结束时,临时对象被销毁,因此在

中获得了引用
wrap.get_nougat()

悬空,使用后会导致未定义的行为。