C ++中的临时,引用和三元运算符

时间:2013-09-20 14:15:21

标签: c++ c++11

我有以下基本代码:

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

YX通过功能 =确定

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

YX通过三元运算符 = 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的析构函数运行两次?

2 个答案:

答案 0 :(得分:11)

您缺少的是您的临时Y正在逐层复制构造到绑定到您的const引用的隐藏临时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"});

答案 1 :(得分:5)

  

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

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

  

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

由于object slicing,它会调用X:foo方法。该对象被切成一个临时的X对象。

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

  

X的析构函数运行两次?

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