假设我有一个包含许多成员的对象:
class Example {
AnotherClass member1;
AnotherClass member2;
YetAnotherClass member3;
...
};
是否有简短的方法来执行以下操作:
foreach(member m: myExample)
m.sharedMethod();
而不是单独访问每一个?
我想我可以将它们放在一个向量中并使用shared_ptr
获得相同的效果,我只是想知道是否说,Boost或其他一些流行的库没有自动执行此操作。
答案 0 :(得分:11)
C ++不支持类内省,所以你不能迭代这样的类中的所有成员 - 不是没有(手动编写的)函数,无论如何都会迭代所有成员。
原则上,您可以像这样添加模板成员:
template<typename Functor>
void doAllMembers(Functor &f) {
f(member1);
f(member2);
f(member3);
}
那就是说,我认为这是一个破碎的设计;你已经离开并公开了所有内部成员。如果您稍后添加一个会怎么样?或者改变一个语义?使一个缓存的值有时会过时?等等,如果你的成员并非都从相同的类型继承,会发生什么?
退一步重新考虑你的设计。
答案 1 :(得分:6)
这个问题有几种解决方案,与反对者的言论相反,但没有内置方式。
C ++在编译时支持有限的内省:你可以检查模板参数。
使用Boost.Tuple
或Boost.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序列化非常容易使用。