3 回答

TA貢獻1811條經驗 獲得超4個贊
另一種方式是這種方式,它也依賴于SFINAE表達式。如果名稱查找導致歧義,編譯器將拒絕該模板
template<typename T> struct HasX { struct Fallback { int x; }; // introduce member name "x" struct Derived : T, Fallback { }; template<typename C, C> struct ChT; template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; template<typename C> static char (&f(...))[2]; static bool const value = sizeof(f<Derived>(0)) == 2;}; struct A { int x; };struct B { int X; };int main() { std::cout << HasX<A>::value << std::endl; // 1 std::cout << HasX<B>::value << std::endl; // 0}
它基于usenet上有人的精彩想法。
注意:HasX檢查任何名為x的數據或函數成員,具有任意類型。引入成員名稱的唯一目的是使成員名稱查找可能存在歧義 - 成員的類型并不重要。

TA貢獻1871條經驗 獲得超13個贊
這里是一個解決方案不是簡單的 約翰內斯·紹布- litb的一個。它需要C ++ 11。
#include <type_traits>template <typename T, typename = int>struct HasX : std::false_type { };template <typename T>struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
更新:一個簡單的例子和解釋如何工作。
對于這些類型:
struct A { int x; };struct B { int y; };
我們有HasX<A>::value == true
和HasX<B>::value == false
。讓我們看看為什么。
首先回想一下,std::false_type
并且std::true_type
有一個static constexpr bool
名為的成員,分別value
設置為false
和true
。因此,HasX
上面的兩個模板繼承了這個成員。(來自std::false_type
的第一個模板和來自的第二個模板std::true_type
。)
讓我們開始簡單,然后一步一步地進行,直到我們得到上面的代碼。
1)起點:
template <typename T, typename U>struct HasX : std::false_type { };
在這種情況下,毫不奇怪:HasX
派生于std::false_type
和因此HasX<bool, double>::value == false
而且HasX<bool, int>::value == false
。
2)違約U
:
// Primary templatetemplate <typename T, typename U = int>struct HasX : std::false_type { };
鑒于U
默認值int
,Has<bool>
實際上意味著HasX<bool, int>
,因此,HasX<bool>::value == HasX<bool, int>::value == false
。
3)添加專業化:
// Primary templatetemplate <typename T, typename U = int>struct HasX : std::false_type { };// Specialization for U = inttemplate <typename T>struct HasX<T, int> : std::true_type { };
一般來說,感謝主要模板,HasX<T, U>
源于std::false_type
。但是,存在一種U = int
衍生自的專業化std::true_type
。因此,HasX<bool, double>::value == false
但是HasX<bool, int>::value == true
。
感謝默認的U
,HasX<bool>::value == HasX<bool, int>::value == true
。
4)decltype
和一種奇特的說法int
:
這里有點偏離,但是,拜托,請耐心等待。
基本上(這不完全正確),decltype(expression)
產生 表達式。例如,因此0
具有類型int
,decltype(0)
意味著int
。類似地,1.2
具有類型double
,因此,decltype(1.2)
意味著double
。
考慮具有此聲明的函數:
char func(foo, int);
哪些foo
是類類型。如果f
是類型的對象foo
,則decltype(func(f, 0))
表示char
(返回的類型func(f, 0)
)。
現在,表達式(1.2, 0)
使用(內置)逗號運算符按順序計算兩個子表達式(即首先1.2
和然后0
),丟棄第一個值并產生第二個值。因此,
int x = (1.2, 0);
相當于
int x = 0;
一起把這個decltype
給出decltype(1.2, 0)
的手段int
。有沒有什么特別的地方1.2
或者double
在這里。例如,true
也有類型bool
和decltype(true, 0)
手段int
。
班級類型怎么樣?對于instace,decltype(f, 0)
意味著什么?人們很自然地認為這仍然意味著int
可能并非如此。實際上,逗號運算符可能有一個重載類似于func
上面的函數,它接受a foo
和a int
并返回a char
。在這種情況下,decltype(foo, 0)
是char
。
我們如何避免使用逗號運算符的重載?好吧,沒有辦法為void
操作數重載逗號運算符,我們可以將任何內容轉換為void
。因此,decltype((void) f, 0)
意味著int
。實際上,(void) f
鑄f
從foo
到void
基本上什么也不做,但說的表達式必須被視為具有類型void
。然后使用內置運算符逗號并生成具有類型的((void) f, 0)
結果。因此,意味著。0
int
decltype((void) f, 0)
int
這個演員真的有必要嗎?好吧,如果逗號運算符沒有超載foo
,int
那么這是沒有必要的。我們總是可以檢查源代碼,看看是否有這樣的運算符。但是,如果它出現在模板中且f
類型V
為模板參數,則不再清楚(甚至不可能知道)逗號運算符的這種重載是否存在。無論如何我們都是通用的。
底線:decltype((void) f, 0)
是一種奇特的說法int
。
5)SFINAE:
這是一門完整的科學;-)好吧,我正在勸告,但這也不是很簡單。所以我會把解釋保持在最低限度。
SFINAE代表替換失敗并非錯誤。這意味著當一個模板參數被一個類型替換時,可能會出現一個非法的C ++代碼,但是在某些情況下,編譯器只是忽略了有問題的代碼,就好像它不存在一樣。讓我們看看它如何適用于我們的案例:
// Primary templatetemplate <typename T, typename U = int>struct HasX : std::false_type { };// Specialization for U = inttemplate <typename T>struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
在這里,再次說,這decltype((void) T::x, 0)
是一種奇特的方式,int
但有SFINAE的好處。
當T
用類型替換時,可能會出現無效的構造。例如,bool::x
無效的C ++,因此T
用bool
in 替換會T::x
產生無效的構造。根據SFINAE原則,編譯器不會拒絕代碼,它只是忽略它的(部分)。更確切地說,正如我們所看到HasX<bool>
的實際含義HasX<bool, int>
。對于專業化U = int
應進行選擇,但同時將其實例化,編譯器發現bool::x
并完全忽略了模板專業化,就好像它不存在。
此時,代碼基本上與上面僅存在主模板的情況(2)相同。因此,HasX<bool, int>::value == false
。
用于相同的論點bool
適用于B
因為B::x
是一個無效的構建體(B
沒有成員x
)。但是,A::x
沒關系,編譯器在實例化U = int
(或者更確切地說是for U = decltype((void) A::x, 0)
)的特化時沒有看到任何問題。因此,HasX<A>::value == true
。
6)取消U
:
好吧,再次查看(5)中的代碼,我們看到該名稱U
不會在其聲明(typename U
)中的任何地方使用。然后我們可以取消命名第二個模板參數,并獲得本文頂部顯示的代碼。
- 3 回答
- 0 關注
- 835 瀏覽
添加回答
舉報