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

发布于 2024-07-24 23:20:28 字数 1140 浏览 13 评论 0原文

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

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

For creating algorithm template function I need to know whether x or X (and y or Y) in class that is template argument. It may by useful when using my function for MFC CPoint class or GDI+ PointF class or some others. All of them use different x in them. My solution could be reduces to the following code:


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

But it does not compile in Visual Studio, while compiling in the GNU C++. With Visual Studio I could use the following template:


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

But it does not compile in GNU C++. Is there universal solution?

UPD: Structures P1 and P2 here are only for example. There are could be any classes with unknown members.

P.S. Please, do not post C++11 solutions here because they are obvious and not relevant to the question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

晨曦慕雪 2024-07-31 23:20:28

这是一个比 更简单的解决方案
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::value == trueHasX::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::value == falseHasX::value == false

2)默认U

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

鉴于U默认为intHas实际上意味着HasX< ;bool, int> 因此,HasX::value == HasX::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 派生自 std::false_type。 然而,U = int 存在一个特化,它派生自 std::true_type。 因此,HasX::value == falseHasX::value == true

感谢 U 的默认设置,HasX::value == HasX::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,然后是 < code>0),丢弃第一个值并产生第二个值。 因此,

int x = (1.2, 0);

相当于

int x = 0;

将其与 decltype 放在一起,得出 decltype(1.2, 0) 表示 int。 这里的 1.2double 没有什么特别之处。 例如,true 的类型为 bool,而 decltype(true, 0) 也表示 int

班级类型怎么样? 例如,decltype(f, 0) 是什么意思? 很自然地期望这仍然意味着int,但情况可能并非如此。 事实上,逗号运算符可能存在重载,类似于上面的函数 func,它接受一个 foo 和一个 int 并返回一个 字符。 在本例中,decltype(foo, 0)char

我们如何避免使用逗号运算符的重载? 嗯,没有办法重载 void 操作数的逗号运算符,我们可以将任何内容转换为 void。 因此,decltype((void) f, 0) 表示 int。 事实上,(void) fffoo 转换为 void,它基本上什么也不做,只是说表达式必须被视为具有 void 类型。 然后使用内置运算符逗号,((void) f, 0) 结果为0,其类型为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 实际上意味着 HasX。 应选择 U = int 的特化,但在实例化它时,编译器会找到 bool::x 并完全忽略模板特化,就好像它不存在一样。

此时,代码与上面情况 (2) 中的代码基本相同,其中仅存在主模板。 因此,HasX::value == false。

用于 bool 的相同参数也适用于 B,因为 B::x 是一个无效的构造(B 没有成员x)。 但是,A::x 是可以的,并且编译器在实例化 U = int 的特化(或者更准确地说,对于 U = decltype( (void)A::x, 0))。 因此,HasX::value == true

6) 取消命名U

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

Here is a solution simpler than
Johannes Schaub - litb
's one. It requires 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 { };

Update: A quick example and the explanation on how this works.

For these types:

struct A { int x; };
struct B { int y; };

we have HasX<A>::value == true and HasX<B>::value == false. Let's see why.

First recall that std::false_type and std::true_type have a static constexpr bool member named value which is set to false and true, respectively. Hence, the two templates HasX above inherit this member. (The first template from std::false_type and the second one from std::true_type.)

Let's start simple and then proceed step by step until we get to the code above.

1) Starting point:

template <typename T, typename U>
struct HasX : std::false_type { };

In this case, there's no surprise: HasX derives from std::false_type and hence HasX<bool, double>::value == false and HasX<bool, int>::value == false.

2) Defaulting U:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

Given that U defaults to int, Has<bool> actually means HasX<bool, int> and thus, HasX<bool>::value == HasX<bool, int>::value == false.

3) Adding a specialization:

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

In general, thanks to the primary template, HasX<T, U> derives from std::false_type. However, there exists a specialization for U = int which derives from std::true_type. Therefore, HasX<bool, double>::value == false but HasX<bool, int>::value == true.

Thanks to the default for U, HasX<bool>::value == HasX<bool, int>::value == true.

4) decltype and a fancy way to say int:

A little digression here but, please, bear with me.

Basically (this is not entirely correct), decltype(expression) yields the type of expression. For instance, 0 has type int thus, decltype(0) means int. Analogously, 1.2 has type double and thus, decltype(1.2) means double.

Consider a function with this declaration:

char func(foo, int);

where foo is some class type. If f is an object of type foo, then decltype(func(f, 0)) means char (the type returned by func(f, 0)).

Now, the expression (1.2, 0) uses the (built-in) comma operator which evaluates the two sub-expressions in order (that is, first 1.2 and then 0), discards the first value and results in the second one. Hence,

int x = (1.2, 0);

is equivalent to

int x = 0;

Putting this together with decltype gives that decltype(1.2, 0) means int. There's nothing really special about 1.2 or double here. For instance, true has type bool and decltype(true, 0) means int as well.

What about a class type? For instace, what does decltype(f, 0) mean? It's natural to expect that this still means int but it might not be the case. Indeed, there might be an overload for the comma operator similar to the function func above that takes a foo and an int and returns a char. In this case, decltype(foo, 0) is char.

How can we avoid the use of a overload for the comma operator? Well, there's no way to overload the comma operator for a void operand and we can cast anything to void. Therefore, decltype((void) f, 0) means int. Indeed, (void) f casts f from foo to void which basically does nothing but saying that the expression must be considered as having type void. Then the built-in operator comma is used and ((void) f, 0) results in 0 which has type int. Hence, decltype((void) f, 0) means int.

Is this cast really necessary? Well, if there's no overload for the comma operator taking foo and int then this isn't necessary. We can always inspect the source code to see if there's such operator or not. However, if this appear in a template and f has type V which is a template parameter, then it's no longer clear (or even impossible to know) whether such overload for the comma operator exists or not. To be generic we cast anyway.

Bottom line: decltype((void) f, 0) is a fancy way to say int.

5) SFINAE:

This is a whole science ;-) OK I'm exagerating but it's not very simple either. So I'll keep the explanation to the bare minimum.

SFINAE stands for Substitution Failure is Not An Error. It means that when a template parameter is substituted by a type, an illegal C++ code might appear but, in some circunstances, instead of aborting compilation the compiler simply ignores the offending code as if it wasn't there. Let's see how it applies to our case:

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

Here, again, decltype((void) T::x, 0) is a fancy way to say int but with the benefit of SFINAE.

When T is substituted with a type, an invalid construct might appear. For instance, bool::x is not valid C++, so substituting T with bool in T::x yields an invalid construct. Under the SFINAE principle, the compiler doesn't reject the code, it simply ignores (parts of) it. More precisely, as we have seenHasX<bool> means actually HasX<bool, int>. The specialization for U = int should be selected but, while instantiating it, the compiler finds bool::x and ignores the template specialization altogether as if it didn't exist.

At this point, the code is essencially the same as in case (2) above where just the primary template exists. Hence, HasX<bool, int>::value == false.

The same argument used for bool holds for B since B::x is an invalid construct (B has no member x). However, A::x is OK and the compiler sees no issue in instantiating the specialization for U = int (or, more precisely, for U = decltype((void) A::x, 0)). Hence, HasX<A>::value == true.

6) Unnaming U:

Well, looking at the code in (5) again, we see that the name U is not used anywhere but in its declaration (typename U). We can then unname the second template argument and we obtain the code shown at the top of this post.

戈亓 2024-07-31 23:20:28

另一种方法是这种方法,它依赖于 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 的任意类型的数据或函数成员。 引入成员名称的唯一目的是使成员名称查找可能存在歧义 - 成员的类型并不重要。

Another way is this one, which relies on SFINAE for expressions too. If the name lookup results in ambiguity, the compiler will reject the template

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
}

It's based on a brilliant idea of someone on usenet.

Note: HasX checks for any data or function member called x, with arbitrary type. The sole purpose of introducing the member name is to have a possible ambiguity for member-name lookup - the type of the member isn't important.

栖迟 2024-07-31 23:20:28

我从一个 问题 重定向到这里,该问题已关闭为这是这个的副本。 我知道这是一个旧线程,但我只是想建议一个与 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 { };

就是这样。 以下是它的使用方式(实例) :

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

I got redirected here from a question which has been closed as a duplicate of this one. I know it's an old thread, but I just wanted to suggest an alternative (simpler?) implementation that works with C++11. Supposing we want to check whether a certain class has a member variable called 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 { };

That's it. And here is how it would be used (live example):

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

Things can be made even simpler with a couple of 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

Which could be used this way:

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;
}
仙气飘飘 2024-07-31 23:20:28

我们可以使用 C++20 require 表达式 来解决这个问题。 h/t 发送给 @lefticus,他最近在 C++ Weekly - Ep 242 - C++20 中的内省设计(概念 + if constexpr

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

你可以看到它 住在这里

We can use a C++20 requires expression to solve this problem. h/t to @lefticus who recently posted this method in C++ Weekly - Ep 242 - Design By Introspection in C++20 (concepts + if constexpr:

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

You can see it live here.

甜扑 2024-07-31 23:20:28

更新:我最近对我在原始答案中发布的代码做了更多操作,因此我正在更新它以考虑更改/添加。

以下是一些使用片段:
*所有这一切的核心是

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

宏(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 {}

//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_ENUM_CHECK: 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)

UPDATE: I've recently done some more with the code I posted in my original answer, so I'm updating this to account for changes/additions.

Here are some usage snippets:
*The guts for all this are farther down

Check for member x in a given class. Could be var, func, class, union, or enum:

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

Check for member function 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;

Check for member variable x:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

Check for member class x:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

Check for member union x:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

Check for member enum x:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

Check for any member function x regardless of signature:

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;

OR

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;

Details and core:

/*
    - 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)
蓝海似她心 2024-07-31 23:20:28

Boost.ConceptTraits 提供了一些宏来定义类型特征,例如 BOOST_TT_EXT_DEFINE_HAS_MEMBER(name),它定义了以下形式的类型特征:

has_member_##name<T>

如果 T 具有名为 的成员类型,则返回 true。 但请注意,这不会检测引用类型成员。

在您的情况下,添加头文件

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)

并进行如下检查

BOOST_STATIC_ASSERT(has_member_x<P1>::value);

就足够了。所使用的技术与前面一些答案中解释的技术相同。

不幸的是这个库不再被维护。 现在 C++0x 将不包含概念,该库与 SFINAE 一起是处理大多数概念的完美替代品。

Boost.ConceptTraits provides between others some macros to define type traits, as for example BOOST_TT_EXT_DEFINE_HAS_MEMBER(name), which defines a type trait of the form:

has_member_##name<T>

This gives true if T has a member type named . Note, however, that this won't detect reference type members.

In you case it will be enough to add in a header file

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)

and check as follows

BOOST_STATIC_ASSERT(has_member_x<P1>::value);

The technique used is the same as the one explained on some of the preceding answers.

Unfortunately this library is no more maintained. Now that C++0x will not includes concept, this library together with SFINAE is a perfect replacement to work with most of the concepts.

(り薆情海 2024-07-31 23:20:28

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

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

Why don't you use specialization like this:

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; }
别低头,皇冠会掉 2024-07-31 23:20:28

函数 (x, X, y, Y) 是否来自抽象基类,或者可以将它们重构为抽象基类吗? 如果是这样,您可以使用现代 C++ 设计中的 SUPERSUBCLASS() 宏,以及这个问题的答案中的想法:

基于编译时类型的调度

Are the functions (x, X, y, Y) from an abstract base class, or could they be refactored to be so? If so you can use the SUPERSUBCLASS() macro from Modern C++ Design, along with ideas from the answer to this question:

Compile-time type based dispatch

没︽人懂的悲伤 2024-07-31 23:20:28

为什么不直接创建 Check_x 的模板专业化?

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

哎呀,当我想到这一点时。 如果您只有两种类型,为什么还需要模板呢?

Why don't you just create template specializations of Check_x ?

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

Heck, when I think of it. If you only have two types, why do you even need templates for this?

﹏雨一样淡蓝的深情 2024-07-31 23:20:28

我们可以在编译时获得: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

对于类/结构:

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

We can get at compile time: 0 - not_member, 1 - is_object, 2 - is_function for each required class and member - object or 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;
}

Result:

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

For class/struct:

struct t {
    int aaa;
    float bbb;
    void func() {}
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文