【问题标题】:C++: Iterating through all of an object's members?C++:遍历对象的所有成员?
【发布时间】:2011-08-22 02:10:04
【问题描述】:

假设我有一个包含许多成员的对象:

class Example {
    AnotherClass member1;
    AnotherClass member2;
    YetAnotherClass member3;
    ...
};

是否有一种简短/简洁的方法来执行以下操作:

foreach(member m: myExample)
    m.sharedMethod();

而不是单独访问每个?

我想我可以将它们放在一个向量中并使用shared_ptr 来获得相同的效果,我只是想知道,Boost 或其他一些流行的库是否无法自动执行此操作。

【问题讨论】:

  • 我真的不认为这是一个好主意。如果你需要迭代某个东西,那么就将它声明为一个可迭代的容器,不然你为什么不这样做呢?
  • 查看 Boost 序列化
  • C++ 没有反射。这意味着这是不可能的。
  • 出于好奇,这有什么用?通常,当某件事根本不可能发生时,是因为它一开始就没有意义。

标签: c++ boost


【解决方案1】:

C++ 不支持类自省,因此您不能像这样遍历类中的所有成员 - 无论如何都不能没有(手动编写的)函数为您遍历所有成员。

原则上,您可以像这样添加模板成员:

template<typename Functor>
void doAllMembers(Functor &f) {
  f(member1);
  f(member2);
  f(member3);
}

也就是说,我认为这是一个失败的设计;你已经公开暴露了你所有的内部成员。如果稍后添加一个会发生什么?或者改变一个的语义?使一个有时过时的缓存值?等等。此外,如果您的成员并非都继承自相同类型,会发生什么情况?

退后一步,重新考虑你的设计。

【讨论】:

    【解决方案2】:

    这个问题有几种解决方案,与反对者的喋喋不休相反,但没有内置的方法。

    C++ 在编译时支持有限的自省:您可以检查模板参数。

    使用Boost.TupleBoost.Fusion(对于它的地图),您确实可以实现您的愿望。在 Boost.Fusion 中,您甚至可以使用 BOOST_FUSION_ADAPT_STRUCT 将基本结构转换为融合序列(并因此对其进行迭代)。

    不过,它需要相当多的模板元编程。

    【讨论】:

    【解决方案3】:

    如果你遵守它的规则并使用模板元编程,C++ 可以做这样的事情。

    不要将你的东西存储在结构或类中,而是将其存储在一个元组中:

    typedef boost::tuple<AnotherClass, AnotherClass, YetAnotherClass> Example;
    

    然后,您可以使用模板元编程算法等(请参阅 Boost.Fusion)来访问成员并查看内容。您可以以模板样式迭代元组的元素。

    【讨论】:

      【解决方案4】:

      您在这里寻找的可能是 Visitor 模式。如果你有一个你描述的对象,有许多重要的成员字段,并且你发现你有许多不同的函数都以相同的方式遍历这个数据结构,那么访问者模式可以非常有助于减少代码重复的数量。它不是自动的,通过,您必须编写遍历所有成员字段的函数,但您只需执行一次,您可以多次使用它与执行不同操作的不同访问者类。

      访问者模式确实需要编写相当多的代码,您需要为访问者提供一个抽象基类:

      class VisitorBase 
      {
          virtual void enter(Example& e)=0;
          virtual void leave(Example& e)=0;
          virtual void enter(AnotherClass& e)=0;
          virtual void leave(AnotherClass& e)=0;
          etc ...
      };
      

      那么你需要在所有要访问的类中接受函数:

      void Example::accept( VisitorBase& visitor ) 
      {
          visitor.enter(*this);
          member1.accept(visitor);
          member2.accept(visitor);
          member3.accept(visitor);
          visitor.leave(*this);
      }
      

      最后,您需要实现具体的访问者类来执行您感兴趣的实际工作,这通常相当于从数据结构中收集信息、对数据结构进行更改或两者兼而有之。 Google 访问者模式,您会在这方面找到很多帮助。

      【讨论】:

        【解决方案5】:

        这是我在 C++11 中实现您想要实现的目标的方式。它使用 tupletemplates。它并不简短,但如果您将一些代码包装在头文件中是可以接受的。 有我的完整可编译示例:

        #include <iostream>
        #include <string>
        using namespace std;
        
        #include <tuple>
        
        //Our iteratation code
        
        //first a iteration helper structure
        template<int N = 0>
        struct IterateP {
          template<class T>
          typename std::enable_if<(N < std::tuple_size<T>::value), void>::type
          iterate(T& t) {
            std::get<N>(t).sharedMethod(); //there is the method name
            IterateP<N+1>().iterate<T>(t);
          }
        
          template<class T>
          typename std::enable_if<!(N < std::tuple_size<T>::value), void>::type
            iterate(T&) {}
        };
        
        //wrapper of the helper structure for a more comfortable usage
        template <typename T>
        void iterate(T& t) { IterateP<>().iterate(t.members); } //look at the .members, is the name of the class tuple
        
        //Helper notation macro. 
        #define MEMBER(name, i) std::tuple_element<i,decltype(members)>::type &name = std::get<i>(members)
        
        //YOUR CLASSES
        
        struct AnotherClass {
          int value;
          void sharedMethod() { cout << value << endl; }
        };
        
        struct YetAnotherClass {
          string value;
          void sharedMethod() { cout << value << endl; }
        };
        
        
        //The class with iterable members
        struct Example {
          std::tuple<AnotherClass, AnotherClass, YetAnotherClass> members; //members must be in a tuple
        
          //These are helper member definition to access the tuple elements as normal members (instance.member1)
          //If you don't you this you need to access the members with tuple classes
          MEMBER(member1, 0); //first param is the member name and the second is it's position in the tuple.
          MEMBER(member2, 1);
          MEMBER(member3, 2);
        };
        
        //USAGE
        int main() {
        
          Example example;
        
          //setting members just as a normal class 
          example.member1.value = 1;
          example.member2.value = 2;
          example.member3.value = "hola";
        
          //magic
          iterate(example);
        }
        

        【讨论】:

          【解决方案6】:

          这在 C++ 中是不可能的,如果你真的非常需要这个,那么请改用 C#。 但我严重怀疑你这样做。

          话虽如此,您真正拥有的唯一选择是使用 .NET 并使用托管 C++,微软称之为托管 C++/CLI。但需要注意的是,您的课程必须是托管的“参考课程”,即托管课程。最终,这一切都被编译为 MSIL,以及与语言无关的中间语言。这是您在运行时使用 .NET 反射来发现它的成员函数和值的唯一方法。然而,即使使用 .NET,它也不像您在上面描述的那样简单。反射的另一个缺点是它很慢,所以如果你大量使用它,你的设计就会很糟糕。 反射很好,因为 .NET 使用它来帮助序列化数据类型,这使得 XML 序列化非常易于使用。

          【讨论】:

            猜你喜欢
            • 2013-10-11
            • 1970-01-01
            • 2021-12-27
            • 1970-01-01
            • 2014-10-07
            • 1970-01-01
            • 2012-08-08
            • 1970-01-01
            • 2019-01-11
            相关资源
            最近更新 更多