According to Herb Sutter's article http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/, the following code is correct:
#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;
}
i.e. the lifetime of v
is extended to the lifetime of the v
const reference.
And indeed this compiles fine with gcc and clang and runs without leaks according to valgrind.
However, when I change the main
function thusly:
int main()
{
const auto& v = f()[3];
cout << v[0] << endl;
}
it still compiles but valgrind warns me of invalid reads in the second line of the function due to the fact that the memory was free'd in the first line.
Is this standard compliant behaviour or could this be a bug in both g++ (4.7.2) and clang (3.5.0-1~exp1)?
If it is standard compliant, it seems pretty weird to me... oh well.
There's no bug here except in your code.
The first example works because, when you bind the result of f()
to v
, you extend the lifetime of that result.
In the second example you don't bind the result of f()
to anything, so its lifetime is not extended. Binding to a subobject of it would count:
[C++11: 12.2/5]:
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except: [..]
…but you're not doing that: you're binding to the result of calling a member function (e.g. operator[]
) on the object, and that result is not a data member of the vector!
(Notably, if you had an std::array
rather than an std::vector
, then the code† would be absolutely fine as array data is stored locally, so elements are subobjects.)
So, you have a dangling reference to a logical element of the original result of f()
which has long gone out of scope.
† Sorry for the horrid initializers but, well, blame C++.