【发布时间】:2012-10-16 04:35:01
【问题描述】:
我们都知道returning a reference to a local variable is a bad idea。但是,我想知道返回引用是否真的是一个好主意,是否有可能确定一些关于何时或何时不这样做的好规则。
我返回引用的问题是调用函数需要关心对象的生命周期,这不应该是它的责任。作为一个人为的例子:
#include <vector>
const int& foo() {
std::vector<int> v = {1, 2, 3, 4, 5};
return v[0];
}
int main(int argc, const char* argv[])
{
const int& not_valid = foo();
return 0;
}
这里,vector 在foo 末尾超出范围,破坏其内容并使对其元素的任何引用无效。 vector::operator[] 返回对元素的引用,因此当进一步从 foo 返回此引用时,main 中的引用悬空。我不相信 const 引用会延长这里的生命周期,因为它不是对临时的引用。
正如我所说,这是一个人为的例子,foo 的作者可能不会那么愚蠢地尝试返回v[0] 作为参考。但是,很容易看出返回引用如何要求调用者关心它不拥有的对象的生命周期。将元素推入 vector 会复制它,因此 vector 负责它。传递引用参数不存在此问题,因为您知道函数将在调用者继续并销毁对象之前完成。
我可以看到返回引用允许使用类似数组的语法,例如 v[0] = 5 - 但是拥有像 v.set(index, value) 这样的成员函数有什么不好呢?至少这样我们就不会暴露内部对象。我知道返回引用也可能会提高性能,但是对于 RVO、命名 RVO (NRVO) 和移动语义,它可以忽略不计或不存在。
所以我一直试图想象在哪些情况下返回引用是真正安全的,但我无法理解它可能涉及的所有不同的所有权语义排列。关于何时执行此操作有什么好的规则吗?
注意:我知道在vectors 中处理所有权的更好方法是使用智能指针,但是使用不同的对象会遇到同样的问题 - 谁拥有智能指针?
【问题讨论】:
-
使用智能指针的不同对象不会有同样的问题。智能指针始终按值存储,存储值的人拥有指针。我没有看到那里的问题。另外,我不知道您所说的返回“真正安全”的参考是什么意思。你能说明为什么
operator[]返回引用不如使用set函数“安全”吗? -
为什么只是引用?相同的参数对指针有效。
-
@sftrabbit 除非您存储了对智能指针的引用,否则这是一个有争议的问题,因为您总是按值存储智能指针,否则它们毫无价值。
-
只考虑“本地”传递。
v是本地的,v包含的任何值也是如此。 -
@sftrabbit 哦,我明白了。在这种情况下,RVO、NRVO 和移动语义绝对不会帮助您,因为您没有返回要从中移动的过期值,或者使用可以通过 RVO 就地构建的临时值。无法复制对象(因为您最终得到 两个,而不是一个),这可能会很昂贵。