介绍
通常T const&
并且可以延长直接绑定到它的临时对象的生命周期,但如果引用“隐藏”在构造函数后面T&&
,则此方法不适用。
由于std::reference_wrapper
是可复制的(有意地),如果std::reference_wrapper
以这样的方式使用引用对象的句柄,则引用对象的句柄可以比临时对象的句柄寿命更长,从而使句柄逃脱了创建临时对象的范围。
这将导致句柄和引用的 对象(即临时对象)之间的生命周期不匹配。
让我们玩“相信”
想象一下下面的非法片段;我们假装它std::reference_wrapper
有一个可以接受临时的构造函数。
让我们也假设传递给构造函数的临时变量将延长其生命周期(即使不是这种情况,在现实生活中它会在之后“死” (1)
)。
typedef std::reference_wrapper<std::string const> string_ref;
string_ref get_ref () {
string_ref temp_ref { std::string { "temporary" } }; // (1)
return temp_ref;
}
int main () {
string_ref val = get_ref ();
val.get (); // the temporary has been deconstructed, this is dangling reference!
}
由于创建了一个具有自动存储持续时间的临时对象,因此它将分配在绑定到范围内的存储上get_ref
。
稍后返回时get_ref
,我们的临时对象将被销毁。这意味着我们的val
inmain
将引用一个无效的对象,因为原始对象不再存在。
以上就是std::reference_wrapper
' 的构造函数没有接受临时变量的重载的原因。
另一个例子
struct A {
A (std::string const& r)
: ref (r)
{ }
std::string const& ref;
};
A foo { std::string { "temporary " } };
foo.ref = ...; // DANGLING REFERENCE!
的生命周期std::string { "temporary" }
不会延长,可以在标准中读取。
12.2p5
临时对象 [class.temporary]
引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象在引用的生命周期内持续存在,但以下情况除外:
临时绑定到构造函数的 ctor-initializer (12.6.2) 中的引用成员将持续存在,直到构造函数退出。
临时绑定到函数调用 (5.2.2) 中的引用参数将持续存在,直到包含调用的完整表达式完成。
临时绑定到函数返回语句 (6.6.3) 中的返回值的生命周期不会延长;临时在 return 语句中的完整表达式的末尾被销毁。
- 临时绑定到new-initializer (5.3.4) 中的引用,直到包含new-initializer的完整表达式完成为止。
注意:重要的是要注意构造函数只不过是一个“花哨”函数,调用对象构造。