猿问

如何检测类中是否有特定的成员变量?

如何检测类中是否有特定的成员变量?

为了创建算法模板函数,我需要知道类中的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仅作为示例。可能有任何具有未知成员的类。


PS请不要在这里发布C ++ 11解决方案,因为它们很明显且与问题无关。


慕少森
浏览 790回答 3
3回答

波斯汪

另一种方式是这种方式,它也依赖于SFINAE表达式。如果名称查找导致歧义,编译器将拒绝该模板template<typename&nbsp;T>&nbsp;struct&nbsp;HasX&nbsp;{&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;Fallback&nbsp;{&nbsp;int&nbsp;x;&nbsp;};&nbsp;//&nbsp;introduce&nbsp;member&nbsp;name&nbsp;"x" &nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;Derived&nbsp;:&nbsp;T,&nbsp;Fallback&nbsp;{&nbsp;}; &nbsp;&nbsp;&nbsp;&nbsp;template<typename&nbsp;C,&nbsp;C>&nbsp;struct&nbsp;ChT;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;template<typename&nbsp;C>&nbsp;static&nbsp;char&nbsp;(&f(ChT<int&nbsp;Fallback::*,&nbsp;&C::x>*))[1];&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;template<typename&nbsp;C>&nbsp;static&nbsp;char&nbsp;(&f(...))[2];&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;bool&nbsp;const&nbsp;value&nbsp;=&nbsp;sizeof(f<Derived>(0))&nbsp;==&nbsp;2;};&nbsp;struct&nbsp;A&nbsp;{&nbsp;int&nbsp;x;&nbsp;};struct&nbsp;B&nbsp;{&nbsp;int&nbsp;X;&nbsp;};int&nbsp;main()&nbsp;{&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;HasX<A>::value&nbsp;<<&nbsp;std::endl;&nbsp;//&nbsp;1 &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;HasX<B>::value&nbsp;<<&nbsp;std::endl;&nbsp;//&nbsp;0}它基于usenet上有人的精彩想法。注意:HasX检查任何名为x的数据或函数成员,具有任意类型。引入成员名称的唯一目的是使成员名称查找可能存在歧义 - 成员的类型并不重要。

慕桂英4014372

这里是一个解决方案不是简单的&nbsp;约翰内斯·绍布- litb的一个。它需要C ++ 11。#include&nbsp;<type_traits>template&nbsp;<typename&nbsp;T,&nbsp;typename&nbsp;=&nbsp;int>struct&nbsp;HasX&nbsp;:&nbsp;std::false_type&nbsp;{&nbsp;};template&nbsp;<typename&nbsp;T>struct&nbsp;HasX&nbsp;<T,&nbsp;decltype((void)&nbsp;T::x,&nbsp;0)>&nbsp;:&nbsp;std::true_type&nbsp;{&nbsp;};更新:一个简单的例子和解释如何工作。对于这些类型:struct&nbsp;A&nbsp;{&nbsp;int&nbsp;x;&nbsp;};struct&nbsp;B&nbsp;{&nbsp;int&nbsp;y;&nbsp;};我们有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&nbsp;<typename&nbsp;T,&nbsp;typename&nbsp;U>struct&nbsp;HasX&nbsp;:&nbsp;std::false_type&nbsp;{&nbsp;};在这种情况下,毫不奇怪:HasX派生于std::false_type和因此HasX<bool, double>::value == false而且HasX<bool, int>::value == false。2)违约U://&nbsp;Primary&nbsp;templatetemplate&nbsp;<typename&nbsp;T,&nbsp;typename&nbsp;U&nbsp;=&nbsp;int>struct&nbsp;HasX&nbsp;:&nbsp;std::false_type&nbsp;{&nbsp;};鉴于U默认值int,Has<bool>实际上意味着HasX<bool, int>,因此,HasX<bool>::value == HasX<bool, int>::value == false。3)添加专业化://&nbsp;Primary&nbsp;templatetemplate&nbsp;<typename&nbsp;T,&nbsp;typename&nbsp;U&nbsp;=&nbsp;int>struct&nbsp;HasX&nbsp;:&nbsp;std::false_type&nbsp;{&nbsp;};//&nbsp;Specialization&nbsp;for&nbsp;U&nbsp;=&nbsp;inttemplate&nbsp;<typename&nbsp;T>struct&nbsp;HasX<T,&nbsp;int>&nbsp;:&nbsp;std::true_type&nbsp;{&nbsp;};一般来说,感谢主要模板,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)产生&nbsp;表达式。例如,因此0具有类型int,decltype(0)意味着int。类似地,1.2具有类型double,因此,decltype(1.2)意味着double。考虑具有此声明的函数:char&nbsp;func(foo,&nbsp;int);哪些foo是类类型。如果f是类型的对象foo,则decltype(func(f, 0))表示char(返回的类型func(f, 0))。现在,表达式(1.2, 0)使用(内置)逗号运算符按顺序计算两个子表达式(即首先1.2和然后0),丢弃第一个值并产生第二个值。因此,int&nbsp;x&nbsp;=&nbsp;(1.2,&nbsp;0);相当于int&nbsp;x&nbsp;=&nbsp;0;一起把这个decltype给出decltype(1.2, 0)的手段int。有没有什么特别的地方1.2或者double在这里。例如,true也有类型bool和decltype(true, 0)手段int。班级类型怎么样?对于instace,decltype(f, 0)意味着什么?人们很自然地认为这仍然意味着int可能并非如此。实际上,逗号运算符可能有一个重载类似于func上面的函数,它接受a&nbsp;foo和a&nbsp;int并返回a&nbsp;char。在这种情况下,decltype(foo, 0)是char。我们如何避免使用逗号运算符的重载?好吧,没有办法为void操作数重载逗号运算符,我们可以将任何内容转换为void。因此,decltype((void) f, 0)意味着int。实际上,(void) f铸f从foo到void基本上什么也不做,但说的表达式必须被视为具有类型void。然后使用内置运算符逗号并生成具有类型的((void) f, 0)结果。因此,意味着。0intdecltype((void) f, 0)int这个演员真的有必要吗?好吧,如果逗号运算符没有超载foo,int那么这是没有必要的。我们总是可以检查源代码,看看是否有这样的运算符。但是,如果它出现在模板中且f类型V为模板参数,则不再清楚(甚至不可能知道)逗号运算符的这种重载是否存在。无论如何我们都是通用的。底线:decltype((void) f, 0)是一种奇特的说法int。5)SFINAE:这是一门完整的科学;-)好吧,我正在劝告,但这也不是很简单。所以我会把解释保持在最低限度。SFINAE代表替换失败并非错误。这意味着当一个模板参数被一个类型替换时,可能会出现一个非法的C ++代码,但是在某些情况下,编译器只是忽略了有问题的代码,就好像它不存在一样。让我们看看它如何适用于我们的案例://&nbsp;Primary&nbsp;templatetemplate&nbsp;<typename&nbsp;T,&nbsp;typename&nbsp;U&nbsp;=&nbsp;int>struct&nbsp;HasX&nbsp;:&nbsp;std::false_type&nbsp;{&nbsp;};//&nbsp;Specialization&nbsp;for&nbsp;U&nbsp;=&nbsp;inttemplate&nbsp;<typename&nbsp;T>struct&nbsp;HasX&nbsp;<T,&nbsp;decltype((void)&nbsp;T::x,&nbsp;0)>&nbsp;:&nbsp;std::true_type&nbsp;{&nbsp;};在这里,再次说,这decltype((void) T::x, 0)是一种奇特的方式,int但有SFINAE的好处。当T用类型替换时,可能会出现无效的构造。例如,bool::x无效的C ++,因此T用boolin&nbsp;替换会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&nbsp;U = decltype((void) A::x, 0))的特化时没有看到任何问题。因此,HasX<A>::value == true。6)取消U:好吧,再次查看(5)中的代码,我们看到该名称U不会在其声明(typename U)中的任何地方使用。然后我们可以取消命名第二个模板参数,并获得本文顶部显示的代码。
随时随地看视频慕课网APP
我要回答