[翻译]More C++ Idioms - 类成员检测器
譯注 - 需要注意的是如果是用VC編譯器,直接使用__if_exist關鍵字就行了,不需要用這種方法:
__if_exist(Class::member){
????//do_something
}
__if_exist(Class::method)
{
????//do_something
}?
目的
檢測一個特定類成員的存在性。?
別稱
動機
編譯期的反射能力是C++模板元編程的基礎。?諸如Boost.TypeTraits和TR1 <type_traits> header的類型特征(Type traits)庫提供了強大的方法來分離類型和他們的關系的信息。檢測一個類的數據成員的存在性也是編譯期反射的一個例子。
解決方案和示例代碼
成員檢測器慣用法(idiom)通過"匹配失敗不是錯誤"(Substitution Failure Is Not An Error-SFINAE)慣用法實現。下面的模板類DetectX<T>是一個可以檢測類型T是不是有一個名為X的數據成員的元函數。注意數據成員X可以是任何類型。
template<typename?T>
class?DetectX
{
????struct?Fallback?{?int?X;?};?//?add?member?name?"X"
????struct?Derived?:?T,?Fallback?{?};
?
????template<typename?U,?U>?struct?Check;
?
????typedef?char?ArrayOfOne[1];??//?typedef?for?an?array?of?size?one.
????typedef?char?ArrayOfTwo[2];??//?typedef?for?an?array?of?size?two.
?
????template<typename?U>?
????static?ArrayOfOne?&?func(Check<int?Fallback::*,?&U::X>?*);
?
????template<typename?U>?
????static?ArrayOfTwo?&?func(...);
?
??public:
????typedef?DetectX?type;
????enum?{?value?=?sizeof(func<Derived>(0))?==?2?};
};//(typedef?DetectX?type; 這個并沒有被用到,刪掉這行也沒有關系)
?
這個慣用法的工作原理是:在編譯期創建一個可控的二義性并通過SFINAE慣用法最終從其中恢復過來。第一個代理類Fallback有一個名字和你想要檢測存在性的成員一樣的成員。類Derived多繼承自T和Fallback,這樣它將有至少一個名為X的成員,如果T也有一個數據成員X的話將會有兩個。
模板結構體Check被用來創建一個可控的二義性。Check需要兩個模板參數,第一個是類型參數,第二個是一個該類型的實例,例如Check<int, 5>就是一個有效地實例化 (譯注:注意例子中用到了指向成員的指針(Pointers to Members)int Fallback::*)。兩個名為func的重載函數創建了一個重載集合,這是SFINAE慣用法的通常做法。第一個func函數只有在數據成員U::X可以被無二義性的取得的情況下會被實例化。而U::X的地址只有在類Derived中只有一個名為X的數據成員時可以被取得。也就是說,此時T不會有名為X的數據成員。如果T含有X,U::X的地址在沒有更多排除歧義的信息的情況下是沒法取得的,因此在沒有錯誤的情況下對第一個func的實例化將會失敗而另一個同名函數將會被選擇。你應該注意到了兩個func函數的返回值類型是不同的。第一個函數返回一個大小為1的數組的引用而第二個函數返回一個大小為2的數組的引用。通過返回值大小的不同可以用來檢測哪個函數被實例化了。最終,一個布爾值被暴露出來(譯注:聲明為public的一個枚舉值,值為0或者1,可當為布爾值使用)。當返回值的sizeof結果為2時該值為true,也就是說,只有當因為T含有名為X的數據成員而導致第二個函數func被實例化時為true。對每一個需要檢測的不同的數據成員,上面的模板需要相應的變化。這時使用一個宏將是一個好辦法。下面的示例代碼說明了宏的使用方法:
#define?CREATE_MEMBER_DETECTOR(X)???????????????????????????????????????????????????\template<typename?T>?class?Detect_##X?{?????????????????????????????????????????????\
????struct?Fallback?{?int?X;?};?????????????????????????????????????????????????????\
????struct?Derived?:?T,?Fallback?{?};???????????????????????????????????????????????\
????????????????????????????????????????????????????????????????????????????????????\
????template<typename?U,?U>?struct?Check;???????????????????????????????????????????\
????????????????????????????????????????????????????????????????????????????????????\
????typedef?char?ArrayOfOne[1];?????????????????????????????????????????????????????\
????typedef?char?ArrayOfTwo[2];?????????????????????????????????????????????????????\
????????????????????????????????????????????????????????????????????????????????????\
????template<typename?U>?static?ArrayOfOne?&?func(Check<int?Fallback::*,?&U::X>?*);?\
????template<typename?U>?static?ArrayOfTwo?&?func(...);?????????????????????????????\
??public:???????????????????????????????????????????????????????????????????????????\
????typedef?Detect_##X?type;????????????????????????????????????????????????????????\
????enum?{?value?=?sizeof(func<Derived>(0))?==?2?};?????????????????????????????????\
};
?
CREATE_MEMBER_DETECTOR(first);
CREATE_MEMBER_DETECTOR(second);
?
int?main(void)
{
??typedef?std::pair<int,?double>?Pair;
??std::cout?<<?((Detect_first<Pair>::value?&&?Detect_second<Pair>::value)??"Pair"?:?"Not?Pair");
}?
?檢測被重載的成員函數
一個成員檢測器慣用法的變體可以用來檢測一個類中特定成員函數的存在性,即使這個函數被重載了(譯注:不被重載的當然也行,因為檢測時函數的參數類型可以作為模板參數傳入,所以可以區分不同的重載)也能被檢測到。
template<typename?T,?typename?RESULT,?typename?ARG1,?typename?ARG2>class?HasPolicy
{
????template?<typename?U,?RESULT?(U::*)(ARG1,?ARG2)>?struct?Check;
????template?<typename?U>?static?char?func(Check<U,?&U::policy>?*);
????template?<typename?U>?static?int?func(...);
??public:
????typedef?HasMember?type;
????enum?{?value?=?sizeof(func<T>(0))?==?sizeof(char)?};?
};
//(typedef?HasMember?type; 似乎是個筆誤,但是刪掉這行也沒有關系)?
?
上面的模板類HasPolicy檢查U是否擁有一個名為policy的成員函數,該函數有兩個參數ARG1和ARG2,返回值為RESULT。模板結構體Check只有在U含有U::policy成員函數,該函數有兩個參數ARG1和ARG2并且返回RESULT時才會實例化成功。 注意模板結構體Check的第一個模板參數是一個類型,第二個模板參數是指向該類型的成員函數的指針。如果模板結構體Check不能被實例化,剩下的以int為返回值的func將會被實例化。func函數返回值類型的大小最終決定類型特征的答案:true或者false.
?
已知應用
相關慣用法
Substitution Failure Is Not An Error (SFINAE)
參考資料
Substitution failure is not an error, part II
原文鏈接
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector?
轉載于:https://www.cnblogs.com/shawnhue/archive/2011/11/29/More_CPP_Idioms_Member_Detector.html
總結
以上是生活随笔為你收集整理的[翻译]More C++ Idioms - 类成员检测器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VHDL 语法小点(1)
- 下一篇: Flex 学习笔记------FLACC