c++ 临时变量问题?
#include <iostream>
#include <string>
using namespace std;
const string& Func() {
return "123";
}
int main() {
string s = Func();
cout << s << endl;
return 0;
}
const
引用不是会提升临时变量的生命期吗? 为什么返回函数内的临时变量会报错了? 我对 const
引用的理解哪里错了吗?
分析
- 第一个函数是返回引用一个临时变量, 函数结束, 临时变量销毁, 引用不存在的东西, 自然出错;
- 第二个函数返回引用的是输入的一个变量的引用, 该变量不属于函数内部的临时变量, 函数结束, 变量仍然存在, 所以没有问题;
#include <iostream>
#include <string>
using namespace std;
const string& Func(const string& s) {
return s;
}
int main() {
string s = Func("123");
cout << s << endl;
return 0;
}
引用不管理生命周期, 谁告诉你可以返回局部自动变量的引用了; 返回引用一般是返回类成员的引用, 全局变量的引用, 堆上内容的引用; 而且你这里多此一举, 返回左值引用后又通过拷贝构造构造一个新的 string.
正确做法:
class Foo {
public:
auto & getName() const & {
return name;
}
private:
std::string name;
};
Foo f;
auto &&name = f.getName();
解释一下轮子哥的代码: 本质也不是引用管理了生命周期, 只不过调用返回值类型的函数时本身就创建了一个匿名对象. C++ 11 中所有作为值类型返回的类型必须有可访问的移动构造函数, 因为从返回值到匿名对象需要调用移动构造函数. 引用只是将其具名化. 匿名对象没被具名化时的生命周期是编译器决定的, 因为没具名化之前代码中只可能去访问到一次匿名对象, 所以一般编译器会选择立刻释放 (或者索性不创建匿名对象 这就是 RVO). 不过其实立刻释放还是等到函数结束一起释放都可以, 所以匿名对象也可以一直存在到函数结束. 只是你再也看不到它而已既然你看不到了, 编译器就有权利"偷偷摸摸"把它优化了. 具名化之后就等于告诉编译器, 我给这个匿名对象命名了编译器就不能偷偷摸摸的因为我看不到它了就把它"优化"了, 不过引用也不会管理这个匿名对象的生命周期, 举个例子如果一个编译器决定在函数结束时才释放所有自动变量, 包括匿名对象, 那么你给一个匿名对象具名化了, 然后把这个引用放到一个 block 里, 这个 block 结束后这个虽然引用就看不到了, 不过匿名对象并不会释放仍旧只会在函数末尾释放. 所以引用并不管理生命周期 (无论是具名对象还是匿名对象的生命周期都不归引用管理), 对匿名对象的引用只是标示了其的可见性, 匿名对象的到底什么时候死还是看编译器.
作者: 知乎用户 29DdNe
链接:https://www.zhihu.com/question/41137408/answer/89778157
来源: 知乎
著作权归作者所有. 商业转载请联系作者获得授权, 非商业转载请注明出处.
你第一个代码其实是这样:
const string& Func() {
string tmp = "123";
return tmp;
//tmp 这里被析构了
}
const
引用提升生命期, 是将临时变量的在 full expression 结束销毁提升为在 const 引用生命结束销毁, 而不是你这个例子的情况.
作者: 冒泡
链接:https://www.zhihu.com/question/41137408/answer/89774776
来源: 知乎
著作权归作者所有. 商业转载请联系作者获得授权, 非商业转载请注明出处.
返回引用是常见的 C++ 误用. 首先 const 引用并没有改变临时变量有效期的作用, 所以返回后引用对象被销毁导致报错. 其次即使对于非临时变量, 返回引用的用法也不好界定, 要么什么作用也没有, 比如你的第二段代码, 要么导致变量所属权模糊, 引发维护性问题. 所以基本上, 除非你十分确定自己想要这种写法, 比如说实现 move 语义, 否则尽量避免返回引用.
文章评论