Reputation: 3391
I have the following code in which I have member v_, a const reference to a vector of ints. It is initialized with an lvalue, so my thinking is this should not cause any copy.
#include <iostream>
#include <iterator>
#include <vector>
using Vec = std::vector<int>;
void out(Vec const& v) {
using namespace std;
cout << "Vector (" << &v[0] << "): [";
ostream_iterator<int> out_it (cout,", ");
copy(v.begin(), v.end(), out_it);
cout << "]\n";
}
struct V {
V(Vec const& v) : v_{v}{}
Vec const& v_;
};
int main(int argc, char** argv) {
Vec v = { 1, 2, 3, 4, 5 };
out(v);
V wrapped { v };
out(wrapped.v_);
}
With clang version 3.6.0 (trunk 225298) I get the following output:
~/tmp$ clang++ -std=c++11 -g -O0 vecmember.cpp
~/tmp$ ./a.out
Vector (0x2480010): [1, 2, 3, 4, 5, ]
Vector (0x2480010): [1, 2, 3, 4, 5, ]
This is what I hoped for. However, with c++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2 I get the following output:
~/tmp$ c++ -std=c++11 -g -O0 vecmember.cpp
~/tmp$ ./a.out
Vector (0xa58010): [1, 2, 3, 4, 5, ]
Vector (0xa58030): []
When I step into the debugger it goes into stl_vector.h function vector(const vector& __x) so it is trying to copy construct the original vector passed to the constructor. Is this a compiler bug or am I somehow doing something undefined or just plain wrong?
In either case, what is a better approach?
Upvotes: 1
Views: 549
Reputation: 137425
The C++11 standard actually said that when you list-initialize a reference like this:
int i;
int & ir{i};
it constructs a temporary and then binds the reference to the temporary. (And in the above example it would fail because a non-const lvalue reference cannot be bound to a temporary.)
Obviously this makes absolutely no sense whatsoever; it's a defect in the standard. Unfortunately, GCC 4.8 implemented this part of the standard to the letter, at least in member initializer lists of constructors. As a result, v_{v}
constructed a temporary std::vector
and bound that temporary to v_
(the binding succeeds because v_
is a const reference); the lifetime of the temporary ends at the end of the constructor, and the reference becomes dangling, meaning that your second out
call has undefined behavior.
The standard defect was fixed by the resolution of CWG issue 1288, and this fix was implemented in GCC 4.9 (see GCC bug 50025). The workaround if you are stuck with GCC 4.8 is to use the old initialization syntax v_(v)
.
Upvotes: 2