C++中的临时、参考和三元运算符

Temporaries, references and ternary operator in C++

本文关键字:运算符 三元 参考 C++      更新时间:2023-10-16

我有以下基本代码:

struct X {
  X(const char* descr) {...}
  ~X() {...} // Not virtual
  virtual void foo() const {...}
};
struct Y : public X {
  Y(const char* descr) {...}
  ~Y() {...} // Not virtual
  virtual void foo() const {...}
};

const X& factory() {
    static X sampleX{"staticX"};
    static Y sampleY{"staticY"};
    return X or Y depending of the test case;
};

和 4 个测试用例:

只需 Y = 正常

const X& var = Y{"temporaryY"};
var.foo();

结果:

X::X() // base temporaryY
Y::Y() // temporaryY
Y::foo()
Y::~Y() // temporaryY
X::~X() // base temporaryY

只需X = 正常

const X& var = X{"temporaryX"};
var.foo();

结果:

X::X() // temporaryX
X::foo()
X::~X() // temporaryX

Y或通过功能X = 确定

const X& var = factory();
var.foo();

结果:

X::X() // staticX
X::X() // base staticY
Y::Y() // staticY
X::foo() or Y::foo()
Y::~Y() // staticY
X::~X() // base staticY
X::~X() // staticX

Y还是通过三元运算符X = WTF?!

const X& var = false ? X{"temporaryX"} : Y{"temporaryY"};
var.foo();

结果:

X::X() // base temporaryY
Y::Y() // temporaryY
Y::~Y() // temporaryY
X::~X() // base temporaryY
X::foo()
X::~X() // base temporaryY

有人可以解释我为什么七个地狱:

  • 在范围结束之前调用Y析构函数?
  • X::foo()被召唤而不是Y::foo()
  • X析构函数运行两次?

您缺少的是,您的临时Y正在按切片复制构造到绑定到您的常量引用的隐藏临时X中。然后,这就是您看到的最后一个析构函数,也解释了为什么Y比预期更早被销毁。制作此副本的原因是三元运算符的"返回"仅是一种类型。X不可能被视为Y因此X是要使用的常见类型,从而引发额外的临时X对象。

请注意,这与"Just Y"测试用例不同,因为在这种情况下,会创建一个临时Y,然后立即尝试绑定到允许的const X&。在三元情况下,运算符本身将中间切片诱导到运算符操作数的公共对象类型,在本例中为 X

我相信您可以通过强制转换为父引用来避免临时切片,但我无法访问 C++11 编译器来测试这一点(除了问题中有些不完整的代码):

const X& var = false ? X{"temporaryX"} : static_cast<const X&>(Y{"temporaryY"});

在范围结束之前调用 Y 的析构函数?

因为创建了 Y 类型的对象,所以必须销毁它。因为它是一个临时对象,所以必须在表达式的末尾(;之后)销毁它。

X::foo() 被调用而不是 Y::foo()?

由于对象切片,它调用X:foo方法。对象被切成一个临时X对象。

这在标准§5.16/3中有解释。

X 的析构函数运行两次?

对于Y临时对象调用一次X析构函数,对var对象调用一次析构函数。