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

Red*_*III 7 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;
};
Run Code Online (Sandbox Code Playgroud)

4个测试用例:

只是Y =好的

const X& var = Y{"temporaryY"};
var.foo();
Run Code Online (Sandbox Code Playgroud)

结果:

X::X() // base temporaryY
Y::Y() // temporaryY
Y::foo()
Y::~Y() // temporaryY
X::~X() // base temporaryY
Run Code Online (Sandbox Code Playgroud)

只是X =好的

const X& var = X{"temporaryX"};
var.foo();
Run Code Online (Sandbox Code Playgroud)

结果:

X::X() // temporaryX
X::foo()
X::~X() // temporaryX
Run Code Online (Sandbox Code Playgroud)

YX通过功能 =确定

const X& var = factory();
var.foo();
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

YX通过三元运营商 = WTF?!

const X& var = false ? X{"temporaryX"} : Y{"temporaryY"};
var.foo();
Run Code Online (Sandbox Code Playgroud)

结果:

X::X() // base temporaryY
Y::Y() // temporaryY
Y::~Y() // temporaryY
X::~X() // base temporaryY
X::foo()
X::~X() // base temporaryY
Run Code Online (Sandbox Code Playgroud)

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

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

Mar*_*k B 11

你所缺少的是你的临时文件Y正在逐层复制构造成一个隐藏的临时文件X,它被绑定到你的const引用.就是你看到的最终析构函数,也解释了为什么它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"});
Run Code Online (Sandbox Code Playgroud)


Pie*_*aud 5

Y的析构函数在作用域结束前调用?

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

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

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

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

X的析构函数运行两次?

X析构函数被调用一次的Y临时对象和一次var.