简体   繁体   中英

const& vector member initialized with const& vector in ctor

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?

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) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM