3

根据 Herb Sutter 的文章http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/,以下代码是正确的:

#include <iostream>
#include <vector>
using namespace std;

vector<vector<int>> f() { return {{1},{2},{3},{4},{5}}; }

int main()
{
    const auto& v = f();
    cout << v[3][0] << endl;
}

即生命周期v延长到v常量引用的生命周期。事实上,根据 valgrind,这可以很好地使用 gcc 和 clang 编译并且运行时没有泄漏。

但是,当我这样更改main功能时:

int main()
{
    const auto& v = f()[3];
    cout << v[0] << endl;
}

它仍然可以编译,但 valgrind 警告我在函数的第二行中读取无效,因为内存在第一行中被释放。

这是符合标准的行为还是可能是 g++ (4.7.2) 和 clang (3.5.0-1~exp1) 中的错误?

如果它符合标准,那对我来说似乎很奇怪......哦,好吧。

4

1 回答 1

11

除了您的代码之外,这里没有错误。

第一个示例有效,因为当您绑定 to 的结果时f()v您延长了该结果的生命周期。

在第二个示例中,您没有将结果绑定f()到任何东西,因此它的生命周期不会延长。绑定到它的子对象将计数:

[C++11: 12.2/5]:第二个上下文是引用绑定到临时的。引用绑定的临时对象或引用绑定的子对象的完整对象的临时对象在引用的生命周期内持续存在,除非:[..]

......但你没有这样做:你绑定到在operator[]对象上调用成员函数(例如)的结果,并且该结果不是向量的数据成员!

(值得注意的是,如果你有 anstd::array而不是std::vector,那么代码绝对没问题,因为数组数据存储在本地,所以元素是子对象。)

因此,您对原始结果的逻辑元素有一个悬空引用,该元素的原始结果f()早已超出范围。

对可怕的初始化程序感到抱歉,但是,好吧,怪罪 C++。

于 2015-01-10T16:27:57.200 回答