以下代码打印
one
、two
、three
。所有 C++ 编译器都希望如此吗?
#include <iostream>
struct Foo
{
const char* m_name;
~Foo() { std::cout << m_name << '\n'; }
};
int main()
{
Foo foo{"three"};
Foo{"one"}; // unnamed object
std::cout << "two" << '\n';
}
临时变量一直存在,直到创建它的完整表达式结束为止。您的变量以分号结束。
这是在 [class.temporary] p4:
临时对象在评估(词法上)包含它们创建点的完整表达式的最后一步被销毁。
您的行为得到保证。
如果满足两个条件,将延长临时材料的使用寿命。第一个是当它是对象的初始值设定项时。第二个是引用绑定到临时对象时。
管理临时对象生命周期的规则与范围的概念无关。范围是name的属性,临时对象没有名称。换句话说,临时对象没有作用域。
大多数情况下,临时对象的生命周期在创建该对象的完整表达式结束时结束,这就是您在实验中观察到的情况。这是一般规则,但也有一些例外。主要的一点是,如果您立即将引用附加到临时对象,则该对象的生命周期将延长以匹配引用的生命周期
const Foo &rfoo = Foo("one");
只要
rfoo
存在,上述临时对象就会存在。
像这样的临时对象的范围只有一行。想一想,行结束后你就不能再引用它了,那为什么这个对象还会残留下来呢?
如果不是这种情况,编译器将无法优化函数调用中的临时对象。
是的,这是需要的。
Foo foo("three")
创建一个普通对象,当作用域结束时该对象将被销毁。
Foo("one")
创建一个临时对象,该对象在指令结束时被销毁[1]。为什么?因为指令结束后您将无法访问它。
[1] 刻意简化:我应该说序列点。
因为标准委员会犯了错误。它这样做是因为他们选择让它这样做。它被定义为这样做。它应该被视为一个匿名实例,其作用域与已命名的实例相同。从实例化点到块的末尾。显然他们认为唯一的用途是将临时数据传递到函数中,在函数调用结束时将其压入堆栈并从堆栈中弹出......
未命名的对象仍应被推入堆栈并保留在堆栈上直到块结束,从而在预期时将其从堆栈中弹出。在单个语句中构造和销毁对象是没有意义的。我希望看到一个实际有用的实例/案例。如果它在块的持续时间内没有保留在范围内,那么它肯定是一个错误,并且至少应该生成一个警告。