Ver*_*tas 2 c++ language-lawyer c++11 c++14 c++17
自从我上次查看临时生命周期规则以来已经有一段时间了,我不记得成员右值引用如何影响生命周期。
例如,采用以下两段代码:
int main()
{
std::get<0>(std::forward_as_tuple(
[](int v){ std::cout << v << std::endl; }
))(6);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
,
int main()
{
auto t = std::forward_as_tuple(
[](int v){ std::cout << v << std::endl; }
);
std::get<0>(t)(6);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果成员右值引用不影响生命周期规则,我希望第一个示例表现良好,而第二个示例未定义(因为包含 lambda 对象的完整表达式以第一个分号结尾)。
C++11、C++14 和 C++17 如何处理给定的示例?三者之间有区别吗?
当您将临时变量直接绑定到除构造函数初始值设定项列表之外的任何位置的引用时,生命周期扩展将适用。(注意:聚合初始化不是构造函数)
std::forward_as_tuple是一个函数。传递给它的任何临时变量的生命周期都不能延长到当前行之外。
基本上,临时变量默认持续到当前行的末尾。(该点实际上并不是当前行的末尾)。在您的两种情况下,它是当前行的末尾( );,临时文件结束其生命周期。对于第一种情况来说这已经足够长了;在第二种情况下,临时变量已死,并且您的代码表现出未定义的行为。
同时:
struct foo {
int&& x;
};
int main() {
foo f{3};
std::cout << f.x << "\n";
}
Run Code Online (Sandbox Code Playgroud)
是完全明确的。没有构造函数,我们将临时值绑定到(右值)引用,从而延长了生命周期。
添加这个:
struct foo {
int&& x;
foo(int&& y):x(y) {}
};
Run Code Online (Sandbox Code Playgroud)
或者
struct foo {
int&& x;
foo(int y):x((int)y) {}
};
Run Code Online (Sandbox Code Playgroud)
现在是UB。
第一个是因为我们在调用构造函数时将临时对象绑定到右值引用。构造函数的内部是无关紧要的,因为没有临时对象直接绑定在那里。然后函数的参数和临时参数都超出了 的范围main。
第二个原因是,将临时(int)y0 绑定到int&&x构造函数初始值设定项列表中的规则不会像其他地方那样延长生命周期。
自 98 以来,临时生命周期扩展的规则在任何版本的 C++ 中都没有改变。我们可能有新的方法来表示临时变量,但一旦它们存在,它们的生命周期就很好理解了。
应该注意的是,您的示例并不真正适用于任何类型的成员引用。您正在使用临时参数调用函数,并且参数是转发引用。因此,所讨论的临时对象绑定到函数参数引用,而不是成员引用。该临时变量的生命周期将在调用该函数的表达式之后结束,就像传递给引用参数的任何临时变量一样。
forward_as_tuple该函数 ( ) 最终将该引用存储在 a 中这一事实tuple是无关紧要的。您对引用所做的操作无法改变其生命周期。
同样,这是 C++98,并且后续版本都没有改变这一点。