【问题标题】:Iterate through Struct and Class Members [duplicate]遍历结构和类成员[重复]
【发布时间】:2013-10-04 05:55:09
【问题描述】:

是否可以在 C++ 中遍历 Struct 或 Class 以查找其所有成员?例如,如果我有 struct a 和 class b:

struct a
{
  int a;
  int b;
  int c;
}

class b
{
  public:
    int a;
    int b;
  private:
    int c;
}

是否有可能让它们循环说得到一个打印语句,说“结构 a 具有名为 a、b、c 的 int”或“b 类具有名为 a、b、c 的 int”

【问题讨论】:

  • 在 C++ 中没有反射。
  • 一般来说不是。如果您在程序中留下了调试信息,调试器可以为您提供该信息,并且您可能能够使用某些库/API 来检查您自己的二进制文件,但这将比几乎任何东西都少有趣...
  • 值得考虑这是否可以作为使用元/模板技术的编译时活动。这些信息应该可供编译器使用 - 尽管在未来的 C++XX 中可能需要支持。

标签: c++ class struct


【解决方案1】:

有几种方法可以做到这一点,但您需要使用一些宏来定义或调整结构。

您可以使用this answer 中给出的REFLECTABLE 宏来定义这样的结构:

struct A
{
    REFLECTABLE
    (
        (int) a,
        (int) b,
        (int) c
    )
};

然后您可以遍历字段并像这样打印每个值:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

A x;
print_fields(x);

另一种方法是将结构调整为融合序列(参见the documentation)。这是一个例子:

struct A
{
    int a;
    int b;
    int c;
};

BOOST_FUSION_ADAPT_STRUCT
(
    A,
    (int, a)
    (int, b)
    (int, c)
)

然后您也可以使用以下方法打印字段:

struct print_visitor
{
    template<class Index, class C>
    void operator()(Index, C & c)
    {

        std::cout << boost::fusion::extension::struct_member_name<C, Index::value>::call() 
                  << "=" 
                  << boost:::fusion::at<Index>(c) 
                  << std::endl;
    }
};


template<class C>
void print_fields(C & c)
{
    typedef boost::mpl::range_c<int,0, boost::fusion::result_of::size<C>::type::value> range;
    boost::mpl::for_each<range>(boost::bind<void>(print_visitor(), boost::ref(c), _1));
}

【讨论】:

    【解决方案2】:

    不,这是不可能的,因为 C++ 中没有反射。

    【讨论】:

    • 我不确定这不是一个答案。他正在告诉 OP 语言限制。这是最有答案的。
    • @CaptainObvlious:答案应该回答问题。这个问题的答案是“不”。我的意思是,答案可以提供一个完整的机制来注释特定结构,以便有人可以迭代一些表示其字段的数据,但这仍然不会迭代结构的字段,它会迭代特殊配置的结构的字段。
    • 这个问题的答案不是“不”。您可以轻松地遍历结构的成员并使用标准 C++ 打印它们:A a; printf(a.a); printf(a.b); printf(a.c);。有很多方法可以使用某种自定义反射机制使语法更像所需的“循环”。
    • @willj:您的代码没有通过对问题的任何合理解释进行迭代(它甚至不会打印提问者要求的内容;-p)。除非您可以使用调试信息,否则自定义反射机制仅适用于已以某种方式注释的结构,而不适用于问题中的示例结构,也不适用于您未定义自己的结构。
    • 多年后,我发现这个答案比所选答案有用得多。真的,答案是no。是的,有一些方法可以解决并达到类似的效果,但我认为除了“极端调试”之外不应使用任何建议。在我看来,对每个类/结构做所有这些额外的工作会在生产应用程序中变得一团糟。
    【解决方案3】:

    如果您有相同类型的成员(就像您在第一个特定示例中所做的那样),并且希望 (a) 有名称,并且 (b) 是可迭代的,那么您可以将数组与枚举组合:

    enum names { alice, bob, carl };
    struct myStruct;
    {
      std::array<int, 3> members;
    }
    

    那么你们两个都可以

    myStruct instance;
    // iterate through them...
    for (auto elem : instance.members)
    {
        // work with each element in sequence
    } 
    // and call them by name, taking away the need to remember which element is the first, etc.
    instance.members[bob] = 100;
    

    显然不是一个通用的解决方案,但我发现这在我自己的工作中很有用。

    【讨论】:

      【解决方案4】:

      如果你的成员变量是相同的类型,你可以做这样的事情,我从 GLM 库中偷来的:

      class Point
      {
          Point();// you must re-implement the default constructor if you need one
      
          union
          {
              struct
              {
                  double x;
                  double y;
                  double z;
              };
              std::array<double, 3> components;
          };
      };
      

      诚然,从可维护性的角度来看,这并不是最优雅的解决方案,手动计算变量的数量是自找麻烦。但是,它无需额外的库或宏即可工作,并且适用于您想要这种行为的大多数情况。

      联合不支持自动生成的默认构造函数,因此您需要编写一个告诉对象如何初始化联合的构造函数。

      for (double component : point.components)
      {
          // do something
      }
      

      【讨论】:

      • 在结构体之后和std:array之前需要有一个分号。编辑队列目前已满,所以我无法编辑问题。
      • 在并集之后也需要分号。
      【解决方案5】:

      假设所有类成员都属于同一类型,您可以使用称为结构化绑定的 C++17 功能。假设所有成员都是公开的,这将起作用:

      struct SI
      {
          int x;
          int y;
          int z;
      };
      
      struct SD
      {
          double x;
          double y;
          double z;
      };
      
      template<typename T>
      void print(const T &val)
      {
          const auto& [a, b, c] = val;
          for (auto elem : {a, b, c})
          {
              std::cout << elem << " ";
          }
          std::cout << std::endl;
      }
      

      这适用于任何具有完全相同(可复制)类型的 3 个公共元素的结构。如果是非公共成员,该功能必须是朋友或成员。然而,这种方法不能轻易扩展到任意数量的元素。

      【讨论】:

        【解决方案6】:

        这是 QCTDevs 答案的改进版本:

        class MyClass
        {
            union
            {
                struct Memberstruct
                {
                    double test0;
                    double test1;
                    double test2;
                } m;
                array<double, sizeof( Memberstruct ) / sizeof( double )> memberarray;
            };
            bool test() { &(m.test1) == &(memberarray[1]); }
        };
        

        要求仍然是所有相同的数据类型,如果需要,您还需要实现默认构造函数。

        改进之处在于无需手动维护数组的大小。

        一个缺点是与没有此解决方法的类相比,语法发生了变化。

        【讨论】:

          猜你喜欢
          • 2019-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-10-01
          • 2019-06-23
          • 2021-12-27
          • 2013-01-03
          • 2018-09-20
          相关资源
          最近更新 更多