为什么临时的寿命不会延长到封闭物体的寿命?

Naw*_*waz 31 c++ temporary

我知道临时不能绑定到非const引用,但它可以绑定到const引用.那是,

 A & x = A(); //error
 const A & y = A(); //ok
Run Code Online (Sandbox Code Playgroud)

我也知道在第二种情况(上图)中,临时创建A()的生命周期延长到const引用的生命周期(即y).

但我的问题是:

可以将绑定到临时的const引用进一步绑定到另一个const引用,将临时的生命周期延长到第二个对象的生命周期吗?

我尝试过这个并没有用.我不太明白这一点.我写了这段代码:

struct A
{
   A()  { std::cout << " A()" << std::endl; }
   ~A() { std::cout << "~A()" << std::endl; }
};

struct B
{
   const A & a;
   B(const A & a) : a(a) { std::cout << " B()" << std::endl; }
   ~B() { std::cout << "~B()" << std::endl; }
};

int main() 
{
        {
            A a;
            B b(a);
        }
        std::cout << "-----" << std::endl;
        {
            B b((A())); //extra braces are needed!
        }
}
Run Code Online (Sandbox Code Playgroud)

输出(ideone):

 A()
 B()
~B()
~A()
-----
 A()
 B()
~A()
~B()
Run Code Online (Sandbox Code Playgroud)

产量差异?为什么临时对象在第二种情况下A()在对象之前被破坏b?标准(C++ 03)是否谈到了这种行为?

Dav*_*eas 19

该标准考虑了延长临时寿命的两种情况:

§12.2/ 4有两种情况,临时表演在不同于完整表达结束时被摧毁.第一个上下文是表达式作为定义对象的声明符的初始值设定项.在该上下文中,保存表达式结果的临时值将持续存在,直到对象的初始化完成.[...]

§12.2/ 5第二个上下文是指引用绑定到临时的.[...]

这两个中没有一个允许您通过稍后将引用绑定到另一个const引用来延长临时的生命周期.但是忽略了标准,并想一想发生了什么:

临时堆在堆栈中创建.从技术上讲,调用约定可能意味着适合寄存器的返回值(临时)甚至可能不会在堆栈中创建,但请耐心等待.当您将常量引用绑定到临时时,编译器会在语义上创建一个隐藏的命名变量(这就是复制构造函数需要可访问的原因,即使它未被调用)并绑定对该变量的引用.实际制作或删除副本是一个细节:我们拥有的是一个未命名的局部变量和对它的引用.

如果标准允许您的用例,则意味着临时的生命周期必须一直延长,直到最后一次引用该变量.现在考虑你的例子的这个简单扩展:

B* f() {
   B * bp = new B(A());
   return b;
}
void test() {
   B* p = f();
   delete p;
}
Run Code Online (Sandbox Code Playgroud)

现在的问题是临时(让我们调用它_T)被绑定f(),它的行为就像一个局部变量.引用内部绑定*bp.现在该对象的生命周期超出了创建临时的函数,但因为_T没有动态分配是不可能的.

在本例中,您可以尝试推理延长临时生命周期所需的工作量,答案是没有某种形式的GC就无法完成.


Bo *_*son 8

不,通过传递引用不会进一步延长延长的生命周期.

在第二种情况下,临时绑定到参数 a,并在参数的生命周期结束时销毁 - 构造函数的结束.

该标准明确指出:

绑定到构造函数的ctor-initializer(12.6.2)中的引用成员的临时绑定将持续存在,直到构造函数退出.


Jam*_*nze 5

§12.2/5 说“第二个上下文 [当临时文件的生命周期延长时] 是引用绑定到临时文件的时候。” 从字面上看,这清楚地表明在您的情况下应该延长寿命;你B::a肯定是暂时的。(引用绑定到一个对象,我看不到它可能绑定到的任何其他对象。)但是,这是非常糟糕的措辞;我确定这意味着“第二个上下文是使用临时对象初始化引用时”,并且延长的生命周期对应于使用创建临时对象的右值表达式初始化的引用的生命周期,而不是稍后可能绑定到对象的任何其他引用的生命周期。就目前而言,措辞需要一些根本无法实现的东西:考虑:

void f(A const& a)
{
    static A const& localA = a;
}
Run Code Online (Sandbox Code Playgroud)

调用:

f(A());
Run Code Online (Sandbox Code Playgroud)

编译器应该放在哪里A()(假设它通常看不到 的代码f(),并且在生成调用时不知道本地静态)?

我认为,实际上,这值得 DR。

我可以补充一点,有文字强烈表明我对意图的解释是正确的。想象一下,您有第二个构造函数B

B::B() : a(A()) {}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,B::a将直接用临时初始化;即使按照我的解释,这个临时的寿命也应该延长。但是,该标准对这种情况进行了特定的例外处理;这种临时性只会持续到构造函数退出(这又会给您留下悬空引用)。这个异常提供了一个非常强烈的迹象,表明标准的作者不打算通过类中的成员引用来延长它们所绑定的任何临时对象的生命周期;同样,动机是可实施性。想象一下,而不是

B b((A()));
Run Code Online (Sandbox Code Playgroud)

你写道:

B* b = new B(A());
Run Code Online (Sandbox Code Playgroud)

编译器应该将临时文件放在哪里,A()以便它的生命周期与动态分配的生命周期相同B