如何在 C++ 初始化列表中将 const 引用成员初始化为另一个成员(std 向量)
How to initialize a const reference member to another member (std vector) in C++ initializer list
我做了以下操作,作为允许通过 numbers
对成员容器 _numbers
进行只读访问的廉价方法:
class Foo {
Foo() : _numbers({}), numbers(_numbers) {
// some code that populates `numbers` via `numbers.push_back(...)`
}
private:
std::vector<int> _numbers;
public:
const std::vector<int>& numbers;
}
但是,这样做我看到 numbers
是空的,而在其他情况下它将包含与 _numbers
.
相同的元素
更准确地说,这似乎是未定义的行为。
在我的真实示例中(这是一个简化版本),我有多个引用容器对与此方案,其中填充的数据在 some 对的 const-reference 成员中可见,并且对于某些人来说不是。
知道这有什么问题吗?非常感谢任何帮助。
编辑这是一个最小的工作示例:
#include <vector>
struct Foo2 {
public:
const int max1;
const int center1;
Foo2(const int max1_);
private:
std::vector<int> _numbers1, _numbers2;
public:
const std::vector<int>& numbers1, numbers2;
};
Foo2::Foo2(const int max1_)
: max1(max1_), center1(max1_/2),
_numbers1({}), _numbers2({}),
numbers1(_numbers1),
numbers2(_numbers2)
{
cout << max1 << endl;
for (int i=0; i<center1; i++) {
_numbers1.push_back(i);
cout << "A: " << i << endl;
}
for (int i=center1; i<max1; i++) {
_numbers2.push_back(i);
cout << "B: " << i << endl;
}
for (int i: numbers1) {
cout << "A: " << i << endl;
}
for (int i: numbers2) {
cout << "B: " << i << endl;
}
}
初始化 Foo2 f(8)
时给出以下输出:
8
A: 0
A: 1
A: 2
A: 3
B: 4
B: 5
B: 6
B: 7
A: 0
A: 1
A: 2
A: 3
即numbers2
看不到 _numbers2
的内容,而 numbers1
似乎有效。
const vector<int>& numbers1, numbers2;
— 这里,只有第一个变量是引用。您需要 &
在第二个变量之前使其也成为参考。然后代码应该可以工作。
但我不得不说你的做法真的很糟糕。您正在为具有内存开销、不可分配性和可能的速度开销的方便语法付费。
改为使用吸气剂:const vector<int>& numbers1() const {return _numbers1;}
。是的,您每次都必须输入额外的 ()
。
嗯...问题的主要原因由@HolyBlackCat给出:numbers2
不是参考而是独立副本。
但是恕我直言,还有一个更根本的问题。有:
public:
const std::vector<int>& numbers;
您承诺编译器,一旦初始化,numbers
引用的向量将不再改变。这是一个谎言,因为底层向量发生了变化...
对编译器撒谎是通往 UB 的必经之路:它有时会工作,但会突然中断,因为编译器可以自由更改其实际代码,因此对 _member
的任何更改都不会反映在 members
.
可能的修复:
使用std::vector<int>& numbers = _numbers;
(没有const
)。这个没问题,但是你失去了提供只读参考的能力
在需要时通过getter(即方法)获得完全初始化的引用:
const std::vector& numbers() {
return _numbers;
}
同样没问题,前提是 _number
在 Foo
完成初始化后不再更改
使用专用对象,可隐式转换为将在使用前完全初始化的向量:
struct Bar {
std::vector<int>_numbers;
Bar(bool first) : Bar(0, first) {};
Bar(int max, bool first) {
cout << max << endl;
int center = max / 2;
if (first) {
for (int i = 0; i < center; i++) {
_numbers.push_back(i);
cout << "A: " << i << endl;
}
}
else {
for (int i = center; i < max; i++) {
_numbers.push_back(i);
cout << "B: " << i << endl;
}
}
}
operator const std::vector<int>& () {
return _numbers;
}
};
struct Foo2 {
public:
const int max1;
const int center1;
Foo2();
Foo2(const int max1_);
private:
Bar _numbers1, _numbers2;
public:
const std::vector<int>& numbers1, &numbers2;
};
我做了以下操作,作为允许通过 numbers
对成员容器 _numbers
进行只读访问的廉价方法:
class Foo {
Foo() : _numbers({}), numbers(_numbers) {
// some code that populates `numbers` via `numbers.push_back(...)`
}
private:
std::vector<int> _numbers;
public:
const std::vector<int>& numbers;
}
但是,这样做我看到 numbers
是空的,而在其他情况下它将包含与 _numbers
.
更准确地说,这似乎是未定义的行为。 在我的真实示例中(这是一个简化版本),我有多个引用容器对与此方案,其中填充的数据在 some 对的 const-reference 成员中可见,并且对于某些人来说不是。
知道这有什么问题吗?非常感谢任何帮助。
编辑这是一个最小的工作示例:
#include <vector>
struct Foo2 {
public:
const int max1;
const int center1;
Foo2(const int max1_);
private:
std::vector<int> _numbers1, _numbers2;
public:
const std::vector<int>& numbers1, numbers2;
};
Foo2::Foo2(const int max1_)
: max1(max1_), center1(max1_/2),
_numbers1({}), _numbers2({}),
numbers1(_numbers1),
numbers2(_numbers2)
{
cout << max1 << endl;
for (int i=0; i<center1; i++) {
_numbers1.push_back(i);
cout << "A: " << i << endl;
}
for (int i=center1; i<max1; i++) {
_numbers2.push_back(i);
cout << "B: " << i << endl;
}
for (int i: numbers1) {
cout << "A: " << i << endl;
}
for (int i: numbers2) {
cout << "B: " << i << endl;
}
}
初始化 Foo2 f(8)
时给出以下输出:
8
A: 0
A: 1
A: 2
A: 3
B: 4
B: 5
B: 6
B: 7
A: 0
A: 1
A: 2
A: 3
即numbers2
看不到 _numbers2
的内容,而 numbers1
似乎有效。
const vector<int>& numbers1, numbers2;
— 这里,只有第一个变量是引用。您需要 &
在第二个变量之前使其也成为参考。然后代码应该可以工作。
但我不得不说你的做法真的很糟糕。您正在为具有内存开销、不可分配性和可能的速度开销的方便语法付费。
改为使用吸气剂:const vector<int>& numbers1() const {return _numbers1;}
。是的,您每次都必须输入额外的 ()
。
嗯...问题的主要原因由@HolyBlackCat给出:numbers2
不是参考而是独立副本。
但是恕我直言,还有一个更根本的问题。有:
public:
const std::vector<int>& numbers;
您承诺编译器,一旦初始化,numbers
引用的向量将不再改变。这是一个谎言,因为底层向量发生了变化...
对编译器撒谎是通往 UB 的必经之路:它有时会工作,但会突然中断,因为编译器可以自由更改其实际代码,因此对 _member
的任何更改都不会反映在 members
.
可能的修复:
使用
std::vector<int>& numbers = _numbers;
(没有const
)。这个没问题,但是你失去了提供只读参考的能力在需要时通过getter(即方法)获得完全初始化的引用:
const std::vector& numbers() { return _numbers; }
同样没问题,前提是
完成初始化后不再更改_number
在Foo
使用专用对象,可隐式转换为将在使用前完全初始化的向量:
struct Bar { std::vector<int>_numbers; Bar(bool first) : Bar(0, first) {}; Bar(int max, bool first) { cout << max << endl; int center = max / 2; if (first) { for (int i = 0; i < center; i++) { _numbers.push_back(i); cout << "A: " << i << endl; } } else { for (int i = center; i < max; i++) { _numbers.push_back(i); cout << "B: " << i << endl; } } } operator const std::vector<int>& () { return _numbers; } }; struct Foo2 { public: const int max1; const int center1; Foo2(); Foo2(const int max1_); private: Bar _numbers1, _numbers2; public: const std::vector<int>& numbers1, &numbers2; };