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

为了创建算法模板函数,我需要知道类中的 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;
}

但是它不在 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 + + 中编译,有通用的解决方案吗?

UPD: 这里的结构 P1和 P2只是例子。可能有任何成员未知的类。

另外,请不要在这里发布 C + + 11解决方案,因为它们是显而易见的,与问题无关。

47896 次浏览

你为什么不像这样使用专业化:

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; }

为什么不创建 Check _ x 的模板专门化呢?

template<> bool Check_x(P1 p) { return true; }
template<> bool Check_x(P2 p) { return false; }

如果只有两种类型,为什么还需要模板呢?

函数(x,X,y,Y)是来自抽象基类,还是可以重构它们?如果是这样的话,你可以使用 Modern C + + Design 中的 SUPERSUBCLASS ()宏,以及这个问题的答案:

基于编译时类型的分派

另一种方法是这个,它也依赖于 表达式的 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 的数据或函数成员。引入成员名的唯一目的是使成员名查找可能具有模糊性——成员的类型并不重要。

加油。ConceptTraits 在其他宏之间提供了一些宏来定义类型特性,例如 BOOST_TT_EXT_DEFINE_HAS_MEMBER(name),它定义了表单的类型特性:

has_member_##name<T>

如果 T 有一个成员类型名为。注意,这不会检测引用类型成员。

在您的情况下,添加一个头文件就足够了

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)

检查如下

BOOST_STATIC_ASSERT(has_member_x<P1>::value);

使用的技巧与前面几个答案中解释的技巧相同。

不幸的是,这个库已经不再维护了。既然 C + + 0x 将不包含概念,那么这个库和 SFINAE 是处理大多数概念的完美替代品。

更新: 我最近已经做了一些更多的代码,我张贴在我的原始答案,所以我更新这个帐户的变化/添加。

下面是一些用法片段: 这一切的勇气都在更深的地方 *

检查给定类中的成员 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;

检查成员变量 x:

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;
};

宏(暗黑破坏神!) :

创建 _ 成员 _ 检查:

//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                                                            \
;                                                                       \
}

创建 _ 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 {}

创建 _ 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 {}

创建 _ 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 {}

创建 _ 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 {}

创建 _ 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 {}

创建 _ 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 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)

我被一个 有个问题重定向到这里,这个 有个问题作为这个的副本被关闭了。我知道这是一个老线索,但我只是想建议一个替代方案(更简单?)使用 C + + 11的实现。假设我们想检查某个类是否有一个名为 id的成员变量:

#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 { };

就是这样。下面是它的使用方法(< em > 活生生的例子 ) :

#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;
}

使用两个宏可以使事情变得更简单:

#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;
}

这里有一个比 Johannes Schaub-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_typestd::true_type有一个名为 valuestatic constexpr bool成员,它分别被设置为 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 == falseHasX<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派生出来。然而,U = int存在一个专门化,它来源于 std::true_type。因此,HasX<bool, double>::value == false而不是 HasX<bool, int>::value == true

感谢 UHasX<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是某种类型。如果 ffoo类型的对象,那么 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)意味着 int1.2double在这里并没有什么特别之处。例如,true有类型 booldecltype(true, 0)意味着 int以及。

类类型呢?例如,decltype(f, 0)是什么意思?我们很自然地认为这仍然意味着 int,但事实可能并非如此。实际上,逗号运算符可能有一个重载,类似于上面的函数 func,它接受一个 foo和一个 int并返回一个 char。在这种情况下,decltype(foo, 0)char

我们如何避免使用逗号运算符的重载?对于 void操作数,没有办法重载逗号运算符,我们可以将任何内容强制转换为 void。因此,decltype((void) f, 0)意味着 int。事实上,(void) fffoo转换为 void,这基本上什么也不做,只是说表达式必须被认为具有 void类型。然后使用内置的操作符逗号,((void) f, 0)生成 void0,其类型为 int。因此,decltype((void) f, 0)意味着 int

这个石膏真的有必要吗?那么,如果没有超载的逗号运算符采取 fooint,那么这是没有必要的。我们总是可以检查源代码,看看是否有这样的操作符或没有。但是,如果这出现在一个模板和 f有类型 V,这是一个模板参数,那么它不再清楚(甚至不可能知道)是否存在这样的逗号运算符重载。无论如何,我们选择通用的。

底线: decltype((void) f, 0)是一个说 int的花哨的方式。

5) SFINAE:

这是一门完整的科学; ——好吧,我夸张了,但它也不是很简单。所以我会尽量少解释。

SFINAE 代表替代失败不是错误。这意味着当一个模板参数被一个类型替换时,可能会出现一个非法的 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时,可能会出现无效的结构。例如,bool::x不是有效的 C + + ,因此在 T::x中用 bool代替 T会产生一个无效的结构。根据 SFINAE 原则,编译器不会拒绝代码,它只是忽略(部分)代码。更准确地说,就像我们看到的 HasX<bool>实际上就是 HasX<bool, int>。应该选择 U = int的专门化,但是在实例化它时,编译器会找到 bool::x并完全忽略模板专门化,就好像它不存在一样。

此时,代码基本上与上面的 case (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)不命名 U:

再次查看(5)中的代码,我们发现除了在其声明(typename U)中,其他任何地方都没有使用名称 U。然后我们可以取消第二个模板参数的名称,并获得本文顶部显示的代码。

我们可以在编译时得到: 每个必需的类和成员对象或函数的 0 - not_member, 1 - is_object, 2 - is_function: http://ideone.com/Fjm9u5

#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

对于 class/struct:

struct t {
int aaa;
float bbb;
void func() {}
};

我们可以使用 C + + 20需要表达式来解决这个问题。 h/t 到 @ left ticus,他最近在 C + + 每周-Ep 242-C + + 20中的自省式设计(概念 + if conexpr中发表了这个方法:

#include <iostream>


struct P1 {int x;};
struct P2 {float X;};


bool has_x(const auto &obj) {
if constexpr (requires {obj.x;}) {
return true;
} else
return false;
}


int main()
{
P1 p1 = {1};
P2 p2 = {1};


std::cout << std::boolalpha << has_x(p1) << "\n";
std::cout << has_x(p2) << "\n";


return 0;
}

你可以看到它 住在这里