auto &&如何延长临时对象的生命周期?

Jam*_*ree 10 c++ temporary lifetime auto c++11

下面的代码说明了我的担忧:

#include <iostream>


struct O
{
    ~O()
    {
        std::cout << "~O()\n";
    }
};

struct wrapper
{
    O const& val;

    ~wrapper()
    {
        std::cout << "~wrapper()\n";
    }
};

struct wrapperEx // with explicit ctor
{
    O const& val;

    explicit wrapperEx(O const& val)
      : val(val)
    {}

    ~wrapperEx()
    {
        std::cout << "~wrapperEx()\n";
    }
};

template<class T>
T&& f(T&& t)
{
    return std::forward<T>(t);
}


int main()
{
    std::cout << "case 1-----------\n";
    {
        auto&& a = wrapper{O()};
        std::cout << "end-scope\n";
    }
    std::cout << "case 2-----------\n";
    {
        auto a = wrapper{O()};
        std::cout << "end-scope\n";
    }
    std::cout << "case 3-----------\n";
    {
        auto&& a = wrapper{f(O())};
        std::cout << "end-scope\n";
    }
    std::cout << "case Ex-----------\n";
    {
        auto&& a = wrapperEx{O()};
        std::cout << "end-scope\n";
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在这里看到它.

据说这auto&&将延长临时物体的使用寿命,但我找不到这条规则的标准词,至少在N3690中没有.

最相关的可能是关于临时对象的第12.2.5节,但不完全是我正在寻找的内容.

那么,auto && life-time扩展规则是否适用于表达式中涉及的所有临时对象,或仅适用于最终结果?

更具体的是a.val,在我们到达案例1的范围结束之前,保证有效(非悬空)?

编辑: 我更新了示例以显示更多案例(3&Ex).

你会看到,只有在案例1中,O的寿命才会延长.

Cas*_*eri 5

与引用相同的方式const:

const auto& a = wrapper{O()};
Run Code Online (Sandbox Code Playgroud)

要么

const wrapper& a = wrapper{O()};
Run Code Online (Sandbox Code Playgroud)

或者也

wrapper&& a = wrapper{O()};
Run Code Online (Sandbox Code Playgroud)

更具体的是a.val,在我们到达案例1的范围结束之前,保证有效(非悬空)?

是的.

这里几乎没有什么特别重要的auto.它只是正确类型()的占位符,wrapper由编译器推导出来.重点是临时绑定到引用的事实.

有关详细信息,请参阅我引用的"最重要的const"的候选人:

通常,临时对象仅持续到它出现的完整表达式的结尾.但是,C++故意指定将临时对象绑定到堆栈上对const的引用会延长临时对象到引用本身生命周期的生命周期

这篇文章是关于C++ 03但是参数仍然有效:临时可以绑定到引用const(但不是对非引用const).在C++ 11中,临时可以绑定到右值引用.在这两种情况下,临时的生命周期都延长到参考的生命周期.

C++ 11标准的相关部分正是OP中提到的那些部分,即12.2 p4和p5:

4 - 有两种情境,其中临时表在与完整表达结束时不同的点被销毁.第一个背景是[...]

5 - 第二个上下文是指引用绑定到临时.[...]

(在这些行之后的子弹点中有一些例外.)

更新 :(关注texasbruce的评论.)

为什么原因O的情况下,2具有寿命短是,我们有auto a = wrapper{O()};(见,有没有&在这里),然后临时的绑定到一个参考.实际上,临时a使用编译器生成的复制构造函数进行复制.因此,临时文件的生命周期不会扩展,并在其出现的完整表达式结束时死亡.

这个特定的例子存在危险,因为它wrapper::val是一个参考.编译器生成的copy-constructor wrapper将绑定a.val到临时val成员绑定的同一对象.此对象也是临时但类型O.然后,当后者临时死亡时,我们~O()在屏幕上看到并a.val摇晃!

对比案例2:

std::cout << "case 3-----------\n";
{
    O o;
    auto a = wrapper{o};
    std::cout << "end-scope\n";
}
Run Code Online (Sandbox Code Playgroud)

输出是(使用gcc编译时使用选项-fno-elide-constructors)

case 3-----------
~wrapper()
end-scope
~wrapper()
~O()
Run Code Online (Sandbox Code Playgroud)

现在暂时wrapper有其val成员必然o.请注意,这o不是暂时的.正如我所说,awrapper临时的副本a.val也是绑定的 o.在范围结束之前临时wrapper死亡,我们~wrapper()在屏幕上看到第一个.

然后范围结束,我们得到end-scope.现在,a而且o必须在施工相反的顺序被破坏,因此,我们看到~wrapper(),当a死了,最后~O()当它的o时候了.这表明a.val不会摇摆不定.

(最后的评论:我曾经习惯于-fno-elide-constructors阻止与复制构造相关的优化,这会使讨论变得复杂,但这是另一个故事.)