在 ctor 中使用 const 向量初始化的 const 向量成员
const& vector member initialized with const& vector in ctor
我有以下代码,其中我有成员 v_,一个 const
对整数向量的引用。它是用左值初始化的,所以我的
认为这不应该引起任何复制。
#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_
是一个 const 引用);临时对象的生命周期在构造函数结束时结束,引用变为悬空,这意味着您的第二个 out
调用具有未定义的行为。
标准缺陷已由 CWG issue 1288, and this fix was implemented in GCC 4.9 (see GCC bug 50025) 的决议修复。如果您坚持使用 GCC 4.8,解决方法是使用旧的初始化语法 v_(v)
.
我有以下代码,其中我有成员 v_,一个 const 对整数向量的引用。它是用左值初始化的,所以我的 认为这不应该引起任何复制。
#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_
是一个 const 引用);临时对象的生命周期在构造函数结束时结束,引用变为悬空,这意味着您的第二个 out
调用具有未定义的行为。
标准缺陷已由 CWG issue 1288, and this fix was implemented in GCC 4.9 (see GCC bug 50025) 的决议修复。如果您坚持使用 GCC 4.8,解决方法是使用旧的初始化语法 v_(v)
.