有没有办法在不显式添加的情况下将对 C++ class 成员的引用添加到向量中?

Is there a way to add references to C++ class members to a vector without explicitly adding them?

我正在尝试确定哪些模式可用于以更多 "compositional" 和更少 "imperative" 的方式声明 C++ class。

我正在尝试编写一个包含多个 MyDescriptor 成员的 class。 MyDescriptor 需要由 MyHost classes 构造函数初始化,它迭代对 MyHost 成员的引用来执行此操作。

有没有办法声明 MyHost 不需要单独添加对容器的 class 成员引用的实现?

class MyDescriptor {
public:
  string name;
  string fqn;
  MyHost(string n) : name(n) {}
  void init(string host) {
    fqn = host + ":" + name;
  }
  void do_thing() {
    // subclasses to special things.
  }

}

class MyHost {
public:
  vector<MyDescriptor*> descriptors;
  string name
  MyHost(string n, vector<MyDescriptor*> d) : name(n),descriptors(d) {
    for (MyDescriptor *d : descriptors) {
      d->init(name);
    }
  }
}

MyHostImpl : public MyHost {
public:
  // descriptors must be accessible as members like this
  MyDescriptor d_1 = MyDescriptor("abc");
  MyDescriptor d_2 = MyDescriptor("123");

  MyHostImpl(string n) : MyHost(n, {&d_1, &d_2}) {} // This is the issue

  void do_thing_1() {
    // UPDATE: This is loose example - but what is important to
    // know is that developers need to access / use descriptors
    // in methods like this.
    d_1.do_thing();
  }

}

理想情况下,我希望有一种方法可以停止显式声明 descriptors 项目;这个 {&d_1, &d_2} 是我想要消除的。我的团队使用了类似的模式,并且在将描述符添加到 class.

之后不小心没有将描述符添加到向量中而感到沮丧

我不确定你会如何想象它会起作用。 C++ 没有反射,因此您无法遍历它的 class 成员并检查它们的类型。所以,抱歉,就我所知不可能。您当然可以使用一些变通方法,例如直接使用底层基本向量而不是将两个描述符声明为成员变量(您有什么理由需要这样做吗?),然后将它们隐式传递到 Base 构造函数中,但是您重新瞄准是不可能的。

编辑:显然可以使用一些黑暗巫毒魔法,请参阅下面的评论。

是的,有一种方法可以直接满足您的要求,至少在 C++14 中是这样,但它涉及到一堆黑巫术。在本次会议 C++ 2018 演讲中:

Better C++14 reflections - Antony Polukhin

A​​ntony 解释了如何能够遍历结构的所有成员并访问其类型。他使用它在某些输出流上打印值,但您可以使用相同的方法将地址推回 std::vector<MyDescriptor*>.

话虽如此 - 我认为您应该避免这种模式。只需首先创建一个 MyDescriptors 向量,然后使用对该向量的引用即可。为什么要使用需要如此多扭曲才能产生的指针向量?

反转构造函数语义。让描述符的构造函数获取宿主引用并将其自身添加到宿主向量,而不是宿主构造函数接受描述符指针:

    MyDescriptor::MyDescriptor(MyDescriptor const&)=delete;
    auto& MyDescriptor::operator=(MyDescriptor const&)=delete;
    MyDescriptor::MyDescriptor(string n, MyHost& h): name{n},fqn{n+":"+h.get_name()}
    {
        h.add(this);
    };

    MyHost::MyHost(string n):name{n}{};
    void MyHost::add(MyDescriptor* d){
        descriptors.push_back(d);
    };
    auto& MyHost::get_name()const{
        return name;
    };

这样你就不会忘记给主机添加描述符否则编译器会哭。

MyHostImpl::MyHostImpl(string n):
    MyHost{n},
    d_1{"abc",*this},/*compiler kills you if you forget this line*/
    d_2{"123",*this}/*compiler kills you if you forget this line*/
{};