在 ctor 中使用 const&vector 初始化的 const&vector 成员



我有以下代码,其中我有成员v_,一个常量引用整数向量。它是用左值初始化的,所以我的认为这不应该引起任何复制。

#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_);
}

使用 clang 版本 3.6.0(主干 225298),我得到以下输出:

~/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, ]

这就是我所希望的。但是,使用 c++(Ubuntu 4.8.2-19ubuntu1)4.8.2,我得到以下输出:

~/tmp$ c++ -std=c++11 -g -O0 vecmember.cpp
~/tmp$ ./a.out
Vector (0xa58010): [1, 2, 3, 4, 5, ]
Vector (0xa58030): []

当我进入调试器时,它会进入stl_vector.h函数vector(const vector&__x),因此它试图复制构造传递给构造函数的原始向量。这是一个编译器错误,还是我以某种方式做了一些未定义的事情或只是完全错误?

无论哪种情况,什么是更好的方法?

C++11 标准实际上说,当你像这样列出初始化引用时:

int i;
int & ir{i};

它构造一个临时的,然后将引用绑定到临时的。(在上面的示例中,它将失败,因为非常量左值引用无法绑定到临时引用。

显然,这完全没有意义;这是标准的缺陷。不幸的是,GCC 4.8 不折不扣地实现了标准的这一部分,至少在构造函数的成员初始值设定项列表中是这样。结果,v_{v}构造了一个临时std::vector并将该临时绑定到v_(绑定成功,因为v_是一个常量引用);临时的生存期在构造函数结束时结束,引用变得悬空,这意味着您的第二个out调用具有未定义的行为。

CWG 问题 1288 的解决方案修复了标准缺陷,此修复在 GCC 4.9 中实施(请参阅 GCC 错误 50025)。如果您坚持使用 GCC 4.8,解决方法是使用旧的初始化语法v_(v)

最新更新