[英]C++: Iterating through all of an object's members?
假設我有一個包含許多成員的對象:
class Example {
AnotherClass member1;
AnotherClass member2;
YetAnotherClass member3;
...
};
是否有簡短的方法來做類似的事情:
foreach(member m: myExample)
m.sharedMethod();
而不是單獨訪問每一個?
我想我可以將它們放在一個向量中並使用shared_ptr
獲得相同的效果,我只是想知道是否說,Boost或其他一些流行的庫沒有自動執行此操作。
C ++不支持類內省,所以你不能迭代類中的所有成員 - 不管沒有(手動編寫)函數,無論如何都會迭代所有成員。
原則上,您可以像這樣添加模板成員:
template<typename Functor>
void doAllMembers(Functor &f) {
f(member1);
f(member2);
f(member3);
}
也就是說,我認為這是一個破碎的設計; 你已經離開並公開了所有內部成員。 如果您稍后添加一個會怎么樣? 或者改變一個語義? 使一個緩存的值有時會過時? 等等,如果您的成員並非都從相同的類型繼承,會發生什么?
退一步重新考慮你的設計。
這個問題有幾種解決方案,與反對者的言論相反,但沒有內置方式。
C ++在編譯時支持有限的內省:您可以檢查模板參數。
使用Boost.Tuple
或Boost.Fusion
(對於它的地圖),你確實可以實現你想要的。 在Boost.Fusion中,您甚至可以使用BOOST_FUSION_ADAPT_STRUCT將基本結構轉換為Fusion序列(從而迭代它)。
它需要相當多的模板元編程。
如果你按照規則玩並使用模板元編程,C ++可以做這樣的事情。
而不是將您的東西存儲在結構或類中,而是將其存儲在元組中:
typedef boost::tuple<AnotherClass, AnotherClass, YetAnotherClass> Example;
然后你可以使用模板元編程算法等(參見Boost.Fusion)來訪問成員並捅東西。 您可以在元組的元素上迭代,模板樣式。
這是我想要在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);
}
您在這里尋找的是訪客模式。 如果你有一個描述的對象,有許多非平凡的成員字段,並且你發現你有許多不同的函數都以相同的方式遍歷這個數據結構,那么訪問者模式可以非常有助於減少代碼重復的數量。 它不是自動的,你必須編寫遍歷所有成員字段的函數,但是你只需要執行一次,並且可以使用不同的訪問者類多次使用它來執行不同的操作。
訪問者模式確實涉及編寫相當多的代碼,您需要為訪問者提供一個抽象基類:
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);
}
最后,您需要實現具體的訪問者類,這些訪問者類會執行您感興趣的實際工作,這通常相當於從數據結構中收集信息,更改數據結構或兩者的組合。 谷歌訪客模式,你會發現很多幫助。
對於C ++來說這是不可能的,如果你真的真的需要這個而不是使用C#。 但我非常懷疑你這樣做。
話雖如此,關於你真正擁有的唯一選擇是使用.NET並使用托管C ++,微軟稱之為托管C ++ / CLI。 但需要注意的是,你的班級必須是一個托管的'ref class',這意味着一個托管類。 最終它全部被編譯成MSIL,以及與語言無關的中間語言。 這是你在運行時使用.NET反射來發現它的成員函數和值的唯一方法。 然而,即使使用.NET,也不像你描述的那樣容易在上面使用它。 反思的另一個缺點是它很慢,所以如果你大量使用它,你的設計就會很糟糕。 反思很好,因為.NET使用它來幫助序列化數據類型,這使得XML序列化非常容易使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.