C ++:迭代所有对象的成员?

时间:2011-08-22 02:10:04

标签: c++ boost

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

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

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

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

而不是单独访问每一个?

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

6 个答案:

答案 0 :(得分:11)

C ++不支持类内省,所以你不能迭代这样的类中的所有成员 - 不是没有(手动编写的)函数,无论如何都会迭代所有成员。

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

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

那就是说,我认为这是一个破碎的设计;你已经离开并公开了所有内部成员。如果您稍后添加一个会怎么样?或者改变一个语义?使一个缓存的值有时会过时?等等,如果你的成员并非都从相同的类型继承,会发生什么?

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

答案 1 :(得分:6)

这个问题有几种解决方案,与反对者的言论相反,但没有内置方式。

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

使用Boost.TupleBoost.Fusion(对于其地图),您确实可以实现您的目标。在Boost.Fusion中,您甚至可以使用BOOST_FUSION_ADAPT_STRUCT将基本结构转换为Fusion序列(从而迭代它)。

但它需要相当多的模板元编程。

答案 2 :(得分:3)

如果按照规则进行游戏并使用模板元编程,C ++可以做到这样的事情。

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

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

然后你可以使用模板元编程算法等(参见Boost.Fusion)来访问成员并捅东西。您可以在元组的元素上迭代,模板样式。

答案 3 :(得分:0)

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

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

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

最后,您需要实现具体的访问者类,这些访问者类会执行您感兴趣的实际工作,这通常相当于从数据结构中收集信息,更改数据结构或两者的组合。谷歌访客模式,你会发现很多帮助。

答案 4 :(得分:0)

这是我在 C ++ 11 中实现您想要实现的目标的方法。它使用元组模板。它不简短,但如果你将一些代码包装在头文件中是可以接受的。 有一个完整的可编辑的例子:

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

答案 5 :(得分:-2)

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

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