![](/img/trans.png)
[英]Reference member binds to a temporary object whose life-time would be shorter than the lifetime of the constructed object
[英]How would auto&& extend the life-time of the temporary object?
下面的代码说明了我的担忧:
#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;
}
看到它住在这里 。
据说auto&&
会延长临时对象的寿命,但我找不到此规则的标准字词,至少在N3690中找不到。
最相关的可能是关于临时对象的12.2.5节,但不完全是我要寻找的对象。
那么,auto &&生命周期扩展规则是适用于表达式中涉及的所有临时对象还是仅适用于最终结果?
更具体地说,在情况1到达作用域末端之前, a.val
保证有效(非悬挂)?
编辑:我更新了示例以显示更多案例(3和Ex)。
您将看到只有在情况1中,O的寿命才会延长。
引用const
方式相同:
const auto& a = wrapper{O()};
要么
const wrapper& a = wrapper{O()};
或者也
wrapper&& a = wrapper{O()};
更具体地说,在情况1到达作用域末端之前,
a.val
保证有效(非悬挂)?
是的。
这里的auto
几乎没有什么特别重要的。 它只是由编译器推断出的正确类型( wrapper
)的占位符 。 要点是,临时对象绑定到引用。
有关更多详细信息,请参见我引用的“最重要const”候选人 :
通常,临时对象仅持续到其出现的完整表达式的结尾。 但是,C ++故意指定将临时对象绑定到堆栈上const的引用会延长临时对象的寿命至引用本身的寿命
本文是关于C ++ 03的,但该参数仍然有效:可以将临时绑定到对const
的引用(但不能绑定到对non- const
的引用)。 在C ++ 11中,临时变量也可以绑定到右值引用。 在这两种情况下,临时项的寿命都延长到参考的寿命。
C ++ 11标准的相关部分正是OP中提到的部分,即12.2 p4和p5:
4-在两种情况下,临时变量在与完整表达式末尾不同的位置被销毁。 第一个上下文是[...]
5-第二种情况是引用绑定到临时项时。 [...]
(这些行后面的项目符号中有一些例外。)
更新 :(根据texasbruce的评论。)
情况2中的O
寿命短的原因是我们有auto a = wrapper{O()};
(请参见,此处没有&
),然后该临时对象就不会绑定到引用。 实际上,临时文件是使用编译器生成的copy-constructor复制到a
。 因此,临时项不会延长其生存期,并且会在出现的完整表达式的末尾死亡。
在此特定示例中存在危险,因为wrapper::val
是引用。 编译器生成的wrapper
copy-constructor会将a.val
绑定到与临时val
成员绑定到的同一对象。 该对象也是临时的,但类型为O
然后,当后一个临时对象死亡时,我们在屏幕上看到a.val
~O()
,并且a.val
悬挂了!
与此进行对比的情况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()
现在,临时wrapper
将其val
成员绑定到o
。 请注意, o
不是临时的。 如我所说, a
是wrapper
临时文件的副本,而a.val
也绑定到o
。 在作用域结束之前,临时wrapper
死亡,我们在屏幕上看到第一个~wrapper()
。
然后范围结束,我们得到end-scope
。 现在, a
和o
必须在施工相反的顺序被破坏,因此,我们看到~wrapper()
时, a
模具和最后~O()
时,它的o
的时间。 这表明a.val
不会a.val
。
(最后再说一句:我已经使用-fno-elide-constructors
,防止复制建设,将这里的讨论复杂化相关的一个优化但这是另一个故事 。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.