c – auto \u0026\u0026如何延长临时对象的生命周期?

下面的代码说明了我的担忧:
#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;
}

看到现场here.

据说汽车&&将延长临时对象的终身时间,但是我在这个规则上找不到标准单词,至少不在N3690中.

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

那么,自动和&终生时间扩展规则适用于表达式中涉及的所有临时对象,还是仅适用于最终结果?

更具体地说,在我们到达范围1之前,a.val保证是有效的(非悬挂)?

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

你会看到,只有在情况1,O的生命周期被延长.

解决方法

与引用const的方式相同:
const auto& a = wrapper{O()};

要么

const wrapper& a = wrapper{O()};

或者也

wrapper&& a = wrapper{O()};

More specific,is a.val guaranteed to be valid (non-dangling) before we reach the end-of-scope in case 1?

是的.

在这里,几乎没有什么特别重要的汽车.它只是编译器推导的正确类型(包装)的占位符.主要的一点是临时被绑定到一个参考.

有关详细信息,请参阅A Candidate For the “Most Important const”我引用:

normally,a temporary object lasts only until the end of the full expression in which it appears. However,C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself

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

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

4 – There are two contexts in which temporaries are destroyed at a
different point than the end of the full expression. The first context
is […]

5 – The second context is when a reference is bound to a temporary. […]

(在这些行后面的项目符号中有一些例外.)

更新:(按照texasbruce的评论.)

情况2的寿命短的原因是我们有自动a =包装{O()}; (见,没有&这里),然后暂时没有绑定到引用.实际上,临时是使用编译器生成的拷贝构造函数进行复制的.因此,暂时的终身没有扩张,在完整的表达结束时就会死亡.

在这个特定的例子中有一个危险,因为wrapper :: val是一个引用.编译器生成的wrapper构造函数将绑定a.val到与临时val成员绑定的同一个对象.这个对象也是一个临时的,但是类型为O.然后,当这个对象暂时死亡时,我们在屏幕上看到〜O(),并且a.val dangles!

对比案例2与此:

std::cout << "case 3-----------\n";
{
    O o;
    auto a = wrapper{o};
    std::cout << "end-scope\n";
}

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

case 3-----------
~wrapper()
end-scope
~wrapper()
~O()

现在临时包装器的val成员绑定到o.注意o不是临时的.正如我所说,a是临时包装的副本,a.val也绑定到
O操作.在范围结束之前,临时包装器死亡,我们在屏幕上看到第一个〜wrapper().

然后范围结束,我们得到终点.现在,a和o必须以相反的构造顺序被销毁,所以我们看到〜wrapper()当一个死亡,最后〜O()当它的时间.这表明a.val不会发生变化.

(最后的话:我已经使用-fno-elide-constructors来防止与复制结构相关的优化,这将使这里的讨论复杂化,但这是另一个story.)

相关文章

对象的传值与返回说起函数,就不免要谈谈函数的参数和返回值...
从实现装饰者模式中思考C++指针和引用的选择最近在看...
关于vtordisp知多少?我相信不少人看到这篇文章,多半是来自...
那些陌生的C++关键字学过程序语言的人相信对关键字并...
命令行下的树形打印最近在处理代码分析问题时,需要将代码的...
虚函数与虚继承寻踪封装、继承、多态是面向对象语言的三大特...