亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何檢測類中是否有特定的成員變量?

如何檢測類中是否有特定的成員變量?

C++
慕少森 2019-07-31 18:06:43
如何檢測類中是否有特定的成員變量?為了創建算法模板函數,我需要知道類中的x或X(和y或Y)是模板參數。當我的函數用于MFC CPoint類或GDI + PointF類或其他類時,它可能很有用。他們都使用不同的x。我的解決方案可以簡化為以下代碼:template<int> struct TT {typedef int type;};template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; }template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::type b = 0) { return false; }struct P1 {int x; };struct P2 {float X; };// it also could be struct P3 {unknown_type X; };int main(){    P1 p1 = {1};    P2 p2 = {1};    Check_x(p1); // must return true    Check_x(p2); // must return false    return 0;}但是在GNU C ++中進行編譯時,它無法在Visual Studio中編譯。使用Visual Studio,我可以使用以下模板:template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; }template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; }但它不能在GNU C ++中編譯。有通用解決方案嗎?UPD:此處的結構P1和P2僅作為示例??赡苡腥魏尉哂形粗蓡T的類。PS請不要在這里發布C ++ 11解決方案,因為它們很明顯且與問題無關。
查看完整描述

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的數據或函數成員,具有任意類型。引入成員名稱的唯一目的是使成員名稱查找可能存在歧義 - 成員的類型并不重要。


查看完整回答
反對 回復 2019-07-31
?
慕桂英4014372

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 == trueHasX<B>::value == false。讓我們看看為什么。

首先回想一下,std::false_type并且std::true_type有一個static constexpr bool名為的成員,分別value設置為falsetrue。因此,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默認值intHas<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也有類型booldecltype(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) fffoovoid基本上什么也不做,但說的表達式必須被視為具有類型void。然后使用內置運算符逗號并生成具有類型的((void) f, 0)結果。因此,意味著。0intdecltype((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 ++,因此Tboolin 替換會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)中的任何地方使用。然后我們可以取消命名第二個模板參數,并獲得本文頂部顯示的代碼。


查看完整回答
反對 回復 2019-07-31
  • 3 回答
  • 0 關注
  • 835 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號