auto&&延长临时对象的生命周期? [英] How would auto&& extend the life-time of the temporary object?

查看:52
本文介绍了auto&&延长临时对象的生命周期?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#include 结构体{~O(){std::cout <<"~O()
";}};结构包装器{O常数&值;~包装器(){std::cout <<"~wrapper()
";}};struct wrapperEx//带有显式构造函数{O常数&值;显式 wrapperEx(O const& val): val(val){}~wrapperEx(){std::cout <<"~wrapperEx()
";}};模板T&&f(T&&t){返回 std::forward(t);}int main(){std::cout <<"案例 1-----------
";{自动&&a = 包装器{O()};std::cout <<"结束范围
";}std::cout <<"案例 2-----------
";{汽车 a = 包装器{O()};std::cout <<"结束范围
";}std::cout <<"案例 3-----------
";{自动&&a = 包装器{f(O())};std::cout <<"结束范围
";}std::cout <<"case Ex-----------
";{自动&&a = wrapperEx{O()};std::cout <<"结束范围
";}返回0;}

此处现场观看.

据说auto&&会延长临时对象的生命周期,但我找不到这个规则的标准词,至少在N3690中没有.

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

那么,将 auto&&生命周期延长规则适用于所有表达式中涉及的临时对象,还是仅适用于最终结果?

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

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

您会看到,只有在情况 1 中,O 的生命周期才会延长.

解决方案

与引用 const 的方式相同:

const auto&a = 包装器{O()};

const 包装器&a = 包装器{O()};

或者也

wrapper&&a = 包装器{O()};

<块引用><块引用>

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

是的,是的.

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

更多详情见我引用的最重要的常量"的候选:

<块引用><块引用>

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

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

C++11 标准的相关部分正是 OP 中引用的部分,即 12.2 p4 和 p5:

<块引用>

4 - 在两种情况下,临时文件被销毁与完整表达式的结尾不同的点.第一个上下文是 [...]

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

(这些行后面的要点中有一些例外.)

更新:(遵循 texasbruce 的评论.)

情况 2 中 O 生命周期短的原因是我们有 auto a = wrapper{O()};(看,没有 & 此处),然后临时文件绑定到引用.实际上,临时文件是使用编译器生成的复制构造函数复制到 a 中的.因此,临时变量的生命周期不会延长,并且会在它出现的完整表达式结束时死亡.

在这个特定示例中存在危险,因为 wrapper::val 是一个引用.编译器生成的 wrapper 复制构造函数将把 a.val 绑定到临时的 val 成员绑定到的同一个对象.这个对象也是一个临时的,但属于 O 类型.然后,当后者暂时死亡时,我们会在屏幕上看到 ~O() 并且 a.val 悬垂!

对比案例2:

std::cout <<"案例 3-----------
";{哦;汽车 a = 包装器{o};std::cout <<"结束范围
";}

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

case 3-----------~包装器()最终范围~包装器()~O()

现在临时wrapperval 成员绑定到o.注意 o 不是临时的.正如我所说,awrapper 临时的副本,a.val 也绑定到o.在作用域结束之前,临时 wrapper 消失,我们在屏幕上看到第一个 ~wrapper().

然后范围结束,我们得到 end-scope.现在,ao 必须以相反的构造顺序销毁,因此我们看到 ~wrapper()a 死了,最后 ~O() 到了 o 的时间.这表明 a.val 没有悬垂.

(最后一点:我使用了 -fno-elide-constructors 来防止与复制构造相关的优化,这会使这里的讨论复杂化,但这是另一个 故事.)

The code below illustrated my concern:

#include <iostream>


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

struct wrapper
{
    O const& val;

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

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

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

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

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


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

See it live here.

It's said that auto&& will extend the life-time of the temporary object, but I can't find the standard words on this rule, at least not in N3690.

The most relevant may be section 12.2.5 about temporary object, but not exactly what I'm looking for.

So, would auto&& life-time extension rule apply to all the temporary objects involved in the expression, or only the final result?

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

Edit: I updated the example to show more cases (3 & Ex).

You'll see that only in case 1 the lifetime of O is extended.

解决方案

In the same way that a reference to const does:

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

or

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

or also

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?

Yes, it is.

There's (almost) nothing particularly important about auto here. It's just a place holder for the correct type (wrapper) which is deduced by the compiler. The main point is the fact that the temporary is bound to a reference.

For more details see A Candidate For the "Most Important const" which I quote:

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

The article is about C++ 03 but the argument is still valid: a temporary can be bound to a reference to const (but not to a reference to non-const). In C++ 11, a temporary can also be bound to an rvalue reference. In both cases, the lifetime of the temporary is extended to the lifetime of the reference.

The relevant parts of the C++11 Standard are exactly those referred in the OP, that is, 12.2 p4 and 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. [...]

(There are some exceptions in the bullet points following these lines.)

Update: (Following texasbruce's comment.)

The reason why the O in case 2 has a short lifespan is that we have auto a = wrapper{O()}; (see, there's no & here) and then the temporary is not bound to a reference. The temporary is, actually, copied into a using the compiler generated copy-constructor. Therefore, the temporary doesn't have its lifetime expanded and dies at the end of the full expression in which it appears.

There's a danger in this particular example because wrapper::val is a reference. The compiler generated copy-constructor of wrapper will bind a.val to the same object that the temporary's val member is bound to. This object is also a temporary but of type O. Then, when this latter temporary dies we see ~O() on the screen and a.val dangles!

Contrast case 2 with this:

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

The output is (when compiled with gcc using option -fno-elide-constructors)

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

Now the temporary wrapper has its val member bound to o. Notice that o is not a temporary. As I said, a is a copy of the wrapper temporary and a.val also binds to o. Before the scope ends the temporary wrapper dies and we see the first ~wrapper() on the screen.

Then the scope ends and we get end-scope. Now, a and o must be destroyed in the reverse order of construction, hence we see ~wrapper() when a dies and finally ~O() when it's o's time. This shows that a.val doesn't dangle.

(Final remark: I've used -fno-elide-constructors to prevent a optimization related to copy-construction that would complicate the discussion here but this is another story.)

这篇关于auto&amp;&amp;延长临时对象的生命周期?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆