mar*_*nus 15 c++ reference lifetime
我想将引用传递给函数.这段代码不起作用,正如我所期望的那样:
struct A {
};
void foo(A& a) {
// do something with a
}
int main(int, char**) {
foo(A());
}
Run Code Online (Sandbox Code Playgroud)
我收到编译错误
A&从类型的右值开始无效初始化类型的非const引用A
但是当我只是将方法添加A& ref()到A下面并在传递它之前调用它时,似乎我可以使用它a.调试时,A对象在foo()被调用后被销毁:
struct A {
A& ref() {
return *this;
}
};
void foo(A& a) {
// do something with a
}
int main(int, char**) {
foo(A().ref());
}
Run Code Online (Sandbox Code Playgroud)
这个有效的代码是否符合标准?调用是否会ref()神奇地延长对象的生命周期直到foo()返回?
rus*_*tyx 21
您的代码完全有效.
在这一行
foo(A().ref());
Run Code Online (Sandbox Code Playgroud)
临时实例A直到语句结束(;).
这就是为什么它是安全的传递A&从返回ref()到foo(只要foo不保存它).
ref() 本身不会延长任何生命周期,但它有助于返回左值引用.
在这种情况下会发生什么foo(A());?这里临时值作为右值传递.在C++中,rvalue不绑定到非const左值引用(即使在C++ 11中,rvalue 引用也不绑定到非const左值引用).
从这篇关于rvalue引用的Visual C++博客文章中:
... C++不希望你不小心修改临时值,但直接在可修改的右值上调用非const成员函数是显式的,所以它是允许的......
A()创建一个类型的临时对象A.该对象一直存在,直到创建它的完整表达式结束.原始代码中的问题不是此临时代码的生命周期; 这是函数将其参数作为非const引用,并且不允许将临时对象作为非const引用传递.最简单的改变是foo通过const引用获取它的参数,如果它适合函数的作用:
void foo(const A&);
int main() {
foo(A());
}
Run Code Online (Sandbox Code Playgroud)
这个问题有几个问题.我将尝试解决所有这些问题:
首先,您不能将临时(prvalue)类型A传递给函数,A&因为非const左值引用不能绑定到rvalues.那是一种语言限制.如果您希望能够传递临时值,则需要使用类型A&&或类型的参数A const&- 后者因为临时值可以绑定到const左值引用.
这个有效的代码是否符合标准?调用是否会
ref()神奇地延长对象的生命周期直到foo()返回?
你的程序根本没有进行终身扩展.来自[class.temp]:
有三种情况,临时表在与完整表达结束时不同的地方被摧毁.第一个上下文是调用默认构造函数来初始化没有相应初始值设定项的数组元素(8.6).第二个上下文是在复制整个数组时调用复制构造函数来复制数组的元素(5.1.5,12.8).[...]第三个上下文是指引用绑定到临时.
这些背景都不适用.我们永远不会在此代码中绑定对临时的引用.ref()绑定*this到一个A&,但*this不是临时的,然后将结果引用简单地传入foo().
考虑一下这个程序的变体:
#include <iostream>
struct A {
A& ref() { return *this; }
~A() { std::cout << "~A()\n"; }
};
int main() {
auto& foo = A().ref();
std::cout << "----\n";
}
Run Code Online (Sandbox Code Playgroud)
打印
~A()
----
Run Code Online (Sandbox Code Playgroud)
说明没有终身延长.
如果不是将结果绑定ref()到引用而是绑定成员:
#include <iostream>
struct A {
A& ref() { return *this; }
int x;
~A() { std::cout << "~A()\n"; }
};
int main() {
auto&& foo = A().x;
std::cout << "----\n";
}
Run Code Online (Sandbox Code Playgroud)
然后我们实际上将临时绑定到引用并且应用第三个上下文 - 引用绑定到的子对象的完整对象的生命周期在引用的生命周期中保持不变.所以此代码打印:
----
~A()
Run Code Online (Sandbox Code Playgroud)