如何在 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;
     }
    

    同样没问题,前提是 _numberFoo

    完成初始化后不再更改
  • 使用专用对象,可隐式转换为将在使用前完全初始化的向量:

      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;
      };