如何检测类中是否有特定的成员variables?
为了创buildalgorithm模板函数,我需要知道类或模板参数中的x或X(以及y或Y)。 当使用我的函数为MFC CPoint类或GDI + PointF类或其他一些时可能会有用。 他们都使用不同的x。 我的解决scheme可以简化为以下代码:
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; }
但是它不能在Visual Studio中编译,而在GNU C ++中编译。 使用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 ++中编译。 有通用的解决scheme吗?
UPD:这里的结构P1和P2只是例子。 有可能是任何类未知的成员。
PS请不要在这里发布C ++ 11解决scheme,因为它们很明显而且与这个问题无关。
另一种方法是这个也依赖于SFINAEexpression式 。 如果名称查找结果不明确,编译器将拒绝该模板
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检查任何types的数据或函数成员x。 引入成员名称的唯一目的是对成员名称查找可能存在歧义 – 成员的types并不重要。
这是一个比Johannes Schaub – litb简单的解决scheme。 它需要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 { };
更新 :一个简单的例子和解释如何工作。
对于这些types:
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
inheritance了这个成员。 (来自std::false_type
的第一个模板和来自std::false_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 template template <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 template template <typename T, typename U = int> struct HasX : std::false_type { }; // Specialization for U = int template <typename T> struct HasX<T, int> : std::true_type { };
一般来说,感谢主模板, HasX<T, U>
派生自std::false_type
。 但是,存在一个从std::true_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)
产生decltype(expression)
的types。 例如, 0
types为int
, decltype(0)
表示int
。 类似地, 1.2
有double
型, decltype(1.2)
意味着double
。
考虑这个声明的一个函数:
char func(foo, int);
其中foo
是一些类的types。 如果f
是foo
types的对象,则decltype(func(f, 0))
表示char
(由func(f, 0)
返回的types)。
现在,expression式(1.2, 0)
使用(内置的)逗号运算符来按顺序(即,前1.2
和后0
)计算两个子expression式,丢弃第一个值并导致第二个。 因此,
int x = (1.2, 0);
相当于
int x = 0;
把它和decltype
放在一起给出decltype(1.2, 0)
表示int
。 没有什么特别的关于1.2
或者这里的double
。 例如, true
具有bool
types, decltype(true, 0)
表示int
。
什么类的类? 例如, decltype(f, 0)
是什么意思? 期望这仍然意味着int
是自然的,但可能并非如此。 事实上,逗号运算符可能会有一个类似于上面的函数func
的重载,它使用foo
和int
并返回一个char
。 在这种情况下, decltype(foo, 0)
是char
。
我们如何避免使用逗号运算符的重载? 那么,没有办法将逗号操作符重载为void
操作数,我们可以抛出任何东西来void
。 因此, decltype((void) f, 0)
表示int
。 事实上, (void) f
将f
从foo
转换为void
,基本上什么也不做,只是expression式必须被认为是void
types。 然后使用内置的运算符逗号, ((void) f, 0)
结果为0
,其types为int
。 因此, decltype((void) f, 0)
表示int
。
这个演员是否真的有必要? 那么,如果没有超载的逗号运算符foo
和int
那么这是没有必要的。 我们总是可以检查源代码,看看是否有这样的操作符。 但是,如果这出现在模板中,并且f
具有作为模板参数的typesV
,则不再清楚(甚至不可能知道)逗号运算符的这种重载是否存在。 无论如何,我们都是通用的。
底线: decltype((void) f, 0)
是int
一种奇特的方式。
5)SFINAE:
这是一个完整的科学;-)好吧,我很exagerating但也不是很简单。 所以我会保持解释的最低限度。
SFINAE代表replace失败不是错误。 这意味着,当一个模板参数被一个typesreplace时,一个非法的C ++代码可能会出现,但是在某些情况下 ,编译器不会中止编译,而是简单的忽略那些不正常的代码。 让我们看看它是如何适用于我们的情况:
// Primary template template <typename T, typename U = int> struct HasX : std::false_type { }; // Specialization for U = int template <typename T> struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
在这里,再次, decltype((void) T::x, 0)
是一个奇特的方式来说int
但与SFINAE的好处。
当T
被typesreplace时,可能会出现无效的构造。 例如, bool::x
不是有效的C ++,因此用T::x
bool
代替T
产生一个无效的结构。 在SFINAE原则下,编译器不拒绝代码,它只是忽略它的一部分。 更确切地说,正如我们所看到的, HasX<bool>
实际上意味着HasX<bool, int>
。 应该selectU = int
的专门化,但是在实例化它时,编译器会发现bool::x
并且完全忽略模板专门化,就好像它不存在一样。
此时,代码与上面情况(2)中主要模板存在的情况基本相同。 因此, HasX<bool, int>::value == false
。
用于bool
的同一个参数对于B
是成立的,因为B::x
是一个无效的结构( B
没有成员x
)。 但是, A::x
是可以的,编译器在实例化U = int
(或者更确切地说,对于U = decltype((void) A::x, 0)
)的特化中没有发现问题。 因此, HasX<A>::value == true
。
6)Unnaming U
:
那么,再次看看(5)中的代码,我们看到名称U
不是在任何地方使用,而是在它的声明中( typename U
)。 然后,我们可以取消第二个模板参数的名称,并获得本文顶部显示的代码。
我从一个已经被closures的问题redirect到这里。 我知道这是一个古老的线程,但我只是想提出一个与C ++ 11一起工作的替代(简单的)实现。 假设我们要检查某个类是否有一个名为id
的成员variables:
#include <type_traits> template<typename T, typename = void> struct has_id : std::false_type { }; template<typename T> struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };
而已。 这里是如何使用( 现场示例 ):
#include <iostream> using namespace std; struct X { int id; }; struct Y { int foo; }; int main() { cout << boolalpha; cout << has_id<X>::value << endl; cout << has_id<Y>::value << endl; }
用几个macros可以使事情变得更简单:
#define DEFINE_MEMBER_CHECKER(member) \ template<typename T, typename V = bool> \ struct has_ ## member : false_type { }; \ template<typename T> \ struct has_ ## member<T, \ typename enable_if< \ !is_same<decltype(declval<T>().member), void>::value, \ bool \ >::type \ > : true_type { }; #define HAS_MEMBER(C, member) \ has_ ## member<C>::value
可以这样使用:
using namespace std; struct X { int id; }; struct Y { int foo; }; DEFINE_MEMBER_CHECKER(foo) int main() { cout << boolalpha; cout << HAS_MEMBER(X, foo) << endl; cout << HAS_MEMBER(Y, foo) << endl; }
更新:我最近做了更多的代码,我张贴在我原来的答案,所以我更新这个帐户的变化/添加。
以下是一些使用片段:*所有这些的胆量越来越低
检查给定类中的成员x
。 可以是var,func,class,union或者enum:
CREATE_MEMBER_CHECK(x); bool has_x = has_member_x<class_to_check_for_x>::value;
检查成员函数void x()
:
//Func signature MUST have T as template variable here... simpler this way :\ CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x); bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;
检查成员variablesx
:
CREATE_MEMBER_VAR_CHECK(x); bool has_var_x = has_member_var_x<class_to_check_for_x>::value;
检查会员类别x
:
CREATE_MEMBER_CLASS_CHECK(x); bool has_class_x = has_member_class_x<class_to_check_for_x>::value;
检查会员联盟x
:
CREATE_MEMBER_UNION_CHECK(x); bool has_union_x = has_member_union_x<class_to_check_for_x>::value;
检查成员枚举x
:
CREATE_MEMBER_ENUM_CHECK(x); bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;
无论签名如何,检查任何成员函数x
:
CREATE_MEMBER_CHECK(x); CREATE_MEMBER_VAR_CHECK(x); CREATE_MEMBER_CLASS_CHECK(x); CREATE_MEMBER_UNION_CHECK(x); CREATE_MEMBER_ENUM_CHECK(x); CREATE_MEMBER_FUNC_CHECK(x); bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
要么
CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above. bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
细节和核心:
/* - Multiple inheritance forces ambiguity of member names. - SFINAE is used to make aliases to member names. - Expression SFINAE is used in just one generic has_member that can accept any alias we pass it. */ template <typename... Args> struct ambiguate : public Args... {}; template<typename A, typename = void> struct got_type : std::false_type {}; template<typename A> struct got_type<A> : std::true_type { typedef A type; }; template<typename T, T> struct sig_check : std::true_type {}; template<typename Alias, typename AmbiguitySeed> struct has_member { template<typename C> static char ((&f(decltype(&C::value))))[1]; template<typename C> static char ((&f(...)))[2]; //Make sure the member name is consistently spelled the same. static_assert( (sizeof(f<AmbiguitySeed>(0)) == 1) , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified." ); static bool const value = sizeof(f<Alias>(0)) == 2; };
macros(El Diablo!):
CREATE_MEMBER_CHECK:
//Check for any member with given name, whether var, func, class, union, enum. #define CREATE_MEMBER_CHECK(member) \ \ template<typename T, typename = std::true_type> \ struct Alias_##member; \ \ template<typename T> \ struct Alias_##member < \ T, std::integral_constant<bool, got_type<decltype(&T::member)>::value> \ > { static const decltype(&T::member) value; }; \ \ struct AmbiguitySeed_##member { char member; }; \ \ template<typename T> \ struct has_member_##member { \ static const bool value \ = has_member< \ Alias_##member<ambiguate<T, AmbiguitySeed_##member>> \ , Alias_##member<AmbiguitySeed_##member> \ >::value \ ; \ }
CREATE_MEMBER_VAR_CHECK:
//Check for member variable with given name. #define CREATE_MEMBER_VAR_CHECK(var_name) \ \ template<typename T, typename = std::true_type> \ struct has_member_var_##var_name : std::false_type {}; \ \ template<typename T> \ struct has_member_var_##var_name< \ T \ , std::integral_constant< \ bool \ , !std::is_member_function_pointer<decltype(&T::var_name)>::value \ > \ > : std::true_type {}
CREATE_MEMBER_FUNC_SIG_CHECK:
//Check for member function with given name AND signature. #define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \ \ template<typename T, typename = std::true_type> \ struct has_member_func_##templ_postfix : std::false_type {}; \ \ template<typename T> \ struct has_member_func_##templ_postfix< \ T, std::integral_constant< \ bool \ , sig_check<func_sig, &T::func_name>::value \ > \ > : std::true_type {}
CREATE_MEMBER_CLASS_CHECK:
//Check for member class with given name. #define CREATE_MEMBER_CLASS_CHECK(class_name) \ \ template<typename T, typename = std::true_type> \ struct has_member_class_##class_name : std::false_type {}; \ \ template<typename T> \ struct has_member_class_##class_name< \ T \ , std::integral_constant< \ bool \ , std::is_class< \ typename got_type<typename T::class_name>::type \ >::value \ > \ > : std::true_type {}
CREATE_MEMBER_UNION_CHECK:
//Check for member union with given name. #define CREATE_MEMBER_UNION_CHECK(union_name) \ \ template<typename T, typename = std::true_type> \ struct has_member_union_##union_name : std::false_type {}; \ \ template<typename T> \ struct has_member_union_##union_name< \ T \ , std::integral_constant< \ bool \ , std::is_union< \ typename got_type<typename T::union_name>::type \ >::value \ > \ > : std::true_type {}
CREATE_MEMBER_ENUM_CHECK:
//Check for member enum with given name. #define CREATE_MEMBER_ENUM_CHECK(enum_name) \ \ template<typename T, typename = std::true_type> \ struct has_member_enum_##enum_name : std::false_type {}; \ \ template<typename T> \ struct has_member_enum_##enum_name< \ T \ , std::integral_constant< \ bool \ , std::is_enum< \ typename got_type<typename T::enum_name>::type \ >::value \ > \ > : std::true_type {}
CREATE_MEMBER_FUNC_CHECK:
//Check for function with given name, any signature. #define CREATE_MEMBER_FUNC_CHECK(func) \ template<typename T> \ struct has_member_func_##func { \ static const bool value \ = has_member_##func<T>::value \ && !has_member_var_##func<T>::value \ && !has_member_class_##func<T>::value \ && !has_member_union_##func<T>::value \ && !has_member_enum_##func<T>::value \ ; \ }
CREATE_MEMBER_CHECKS:
//Create all the checks for one member. Does NOT include func sig checks. #define CREATE_MEMBER_CHECKS(member) \ CREATE_MEMBER_CHECK(member); \ CREATE_MEMBER_VAR_CHECK(member); \ CREATE_MEMBER_CLASS_CHECK(member); \ CREATE_MEMBER_UNION_CHECK(member); \ CREATE_MEMBER_ENUM_CHECK(member); \ CREATE_MEMBER_FUNC_CHECK(member)
Boost.ConceptTraits提供了其他的一些macros来定义types特征,例如BOOST_TT_EXT_DEFINE_HAS_MEMBER(name)
,它定义了表单的types特征:
has_member_##name<T>
如果T有一个名为的成员types,则这个值为真。 但请注意,这不会检测引用types成员。
在你的情况下,它将足以添加一个头文件
BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)
并检查如下
BOOST_STATIC_ASSERT(has_member_x<P1>::value);
所使用的技术与前面某些答案中解释的技术相同。
不幸的是这个图书馆没有更多的维护 既然C ++ 0x不会包含概念,那么这个库与SFINAE一起是大多数概念的完美替代品。
你为什么不使用这样的专业化:
struct P1 {int x; }; struct P2 {int X; }; template<class P> bool Check_x(P p) { return true; } template<> bool Check_x<P2>(P2 p) { return false; }
第二个答案(litb的),这表明如何检测一个成员:
是否可以编写一个模板来检查函数的存在?
你为什么不创buildCheck_x的模板特化?
template<> bool Check_x(P1 p) { return true; } template<> bool Check_x(P2 p) { return false; }
哎呀,当我想到它。 如果你只有两种types,为什么你甚至需要模板呢?
是来自抽象基类的函数(x,X,y,Y),还是可以被重构为如此? 如果是这样的话,你可以使用Modern C ++ Design的SUPERSUBCLASS()macros,以及这个问题的答案。
基于编译时types的调度
我们可以在编译时获得: 0 - not_member, 1 - is_object, 2 - is_function
对于每个所需的类和成员 – 对象或函数: http : 0 - not_member, 1 - is_object, 2 - is_function
#include <iostream> #include <type_traits> #define IS_MEMBER(T1, M) \ struct { \ struct verystrangename1 { bool M; }; \ template<typename T> struct verystrangename2 : verystrangename1, public T { }; \ \ enum return_t { not_member, is_object, is_function }; \ template<typename T, typename = decltype(verystrangename2<T>::M)> constexpr return_t what_member() { return not_member; } \ template<typename T> typename std::enable_if<std::is_member_object_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_object; } \ template<typename T> typename std::enable_if<std::is_member_function_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_function; } \ constexpr operator return_t() { return what_member<T1>(); } \ } struct t { int aaa; float bbb; void func() {} }; // Can't be in function IS_MEMBER(t, aaa) is_aaa_member_of_t; IS_MEMBER(t, ccc) is_ccc_member_of_t; IS_MEMBER(t, func) is_func_member_of_t; // known at compile time enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t }; static constexpr int const_is_func_member_of_t = is_func_member_of_t; int main() { std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" << "is aaa member of t = " << is_aaa_member_of_t << std::endl << "is ccc member of t = " << is_ccc_member_of_t << std::endl << "is func member of t = " << is_func_member_of_t << std::endl << std::endl; return 0; }
结果:
0 - not_member, 1 - is_object, 2 - is_function is aaa member of t = 1 is ccc member of t = 0 is func member of t = 2
对于类/结构体:
struct t { int aaa; float bbb; void func() {} };