[翻译]More C++ Idioms - 类成员检测器
譯注 - 需要注意的是如果是用VC編譯器,直接使用__if_exist關(guān)鍵字就行了,不需要用這種方法:
__if_exist(Class::member){
????//do_something
}
__if_exist(Class::method)
{
????//do_something
}?
目的
檢測一個特定類成員的存在性。?
別稱
動機
編譯期的反射能力是C++模板元編程的基礎(chǔ)。?諸如Boost.TypeTraits和TR1 <type_traits> header的類型特征(Type traits)庫提供了強大的方法來分離類型和他們的關(guān)系的信息。檢測一個類的數(shù)據(jù)成員的存在性也是編譯期反射的一個例子。
解決方案和示例代碼
成員檢測器慣用法(idiom)通過"匹配失敗不是錯誤"(Substitution Failure Is Not An Error-SFINAE)慣用法實現(xiàn)。下面的模板類DetectX<T>是一個可以檢測類型T是不是有一個名為X的數(shù)據(jù)成員的元函數(shù)。注意數(shù)據(jù)成員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; 這個并沒有被用到,刪掉這行也沒有關(guān)系)
?
這個慣用法的工作原理是:在編譯期創(chuàng)建一個可控的二義性并通過SFINAE慣用法最終從其中恢復(fù)過來。第一個代理類Fallback有一個名字和你想要檢測存在性的成員一樣的成員。類Derived多繼承自T和Fallback,這樣它將有至少一個名為X的成員,如果T也有一個數(shù)據(jù)成員X的話將會有兩個。
模板結(jié)構(gòu)體Check被用來創(chuàng)建一個可控的二義性。Check需要兩個模板參數(shù),第一個是類型參數(shù),第二個是一個該類型的實例,例如Check<int, 5>就是一個有效地實例化 (譯注:注意例子中用到了指向成員的指針(Pointers to Members)int Fallback::*)。兩個名為func的重載函數(shù)創(chuàng)建了一個重載集合,這是SFINAE慣用法的通常做法。第一個func函數(shù)只有在數(shù)據(jù)成員U::X可以被無二義性的取得的情況下會被實例化。而U::X的地址只有在類Derived中只有一個名為X的數(shù)據(jù)成員時可以被取得。也就是說,此時T不會有名為X的數(shù)據(jù)成員。如果T含有X,U::X的地址在沒有更多排除歧義的信息的情況下是沒法取得的,因此在沒有錯誤的情況下對第一個func的實例化將會失敗而另一個同名函數(shù)將會被選擇。你應(yīng)該注意到了兩個func函數(shù)的返回值類型是不同的。第一個函數(shù)返回一個大小為1的數(shù)組的引用而第二個函數(shù)返回一個大小為2的數(shù)組的引用。通過返回值大小的不同可以用來檢測哪個函數(shù)被實例化了。最終,一個布爾值被暴露出來(譯注:聲明為public的一個枚舉值,值為0或者1,可當(dāng)為布爾值使用)。當(dāng)返回值的sizeof結(jié)果為2時該值為true,也就是說,只有當(dāng)因為T含有名為X的數(shù)據(jù)成員而導(dǎo)致第二個函數(shù)func被實例化時為true。對每一個需要檢測的不同的數(shù)據(jù)成員,上面的模板需要相應(yīng)的變化。這時使用一個宏將是一個好辦法。下面的示例代碼說明了宏的使用方法:
#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");
}?
?檢測被重載的成員函數(shù)
一個成員檢測器慣用法的變體可以用來檢測一個類中特定成員函數(shù)的存在性,即使這個函數(shù)被重載了(譯注:不被重載的當(dāng)然也行,因為檢測時函數(shù)的參數(shù)類型可以作為模板參數(shù)傳入,所以可以區(qū)分不同的重載)也能被檢測到。
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; 似乎是個筆誤,但是刪掉這行也沒有關(guān)系)?
?
上面的模板類HasPolicy檢查U是否擁有一個名為policy的成員函數(shù),該函數(shù)有兩個參數(shù)ARG1和ARG2,返回值為RESULT。模板結(jié)構(gòu)體Check只有在U含有U::policy成員函數(shù),該函數(shù)有兩個參數(shù)ARG1和ARG2并且返回RESULT時才會實例化成功。 注意模板結(jié)構(gòu)體Check的第一個模板參數(shù)是一個類型,第二個模板參數(shù)是指向該類型的成員函數(shù)的指針。如果模板結(jié)構(gòu)體Check不能被實例化,剩下的以int為返回值的func將會被實例化。func函數(shù)返回值類型的大小最終決定類型特征的答案:true或者false.
?
已知應(yīng)用
相關(guān)慣用法
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?
轉(zhuǎn)載于:https://www.cnblogs.com/shawnhue/archive/2011/11/29/More_CPP_Idioms_Member_Detector.html
總結(jié)
以上是生活随笔為你收集整理的[翻译]More C++ Idioms - 类成员检测器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VHDL 语法小点(1)
- 下一篇: s3c2440移植MQTT