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

83

为了创建算法模板函数,我需要知道在作为模板参数的类中是否存在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++中它无法编译。有普适的解决方案吗?

更新:这里的P1和P2结构仅供示例。可能会有任何具有未知成员的类。

附注:请不要在此处发布C++11解决方案,因为它们是显而易见且与问题无关。


我不认为第二种方式是标准的(整数常量表达式可能不能使用op&涉及操作数的op==)。但第一种方式看起来是正确的。MSVC++对此有何说法? - Johannes Schaub - litb
@litb:看一下我回答末尾的链接——我认为它解释了问题(编译器为什么会拒绝它,以及它是否真的被C++98标准允许)。 - James Hopkin
1
+1:有趣的挑战 :-) - James Hopkin
我已经写了一个详细的解释,说明了正确的代码来解决这个问题,并且可以在这里找到:http://cpptalk.wordpress.com/2009/09/12/substitution-failure-is-not-an-error-2/。 很抱歉我重复发表了这条评论,但我觉得它应该放在主帖下面。 - rmn
以下文章解释了如何检测是否存在成员变量,即使它是私有的。https://dev59.com/F73pa4cB1Zd3GeqPfX96?noredirect=1#comment113421398_64139547 - User12547645
10个回答

134

这里有一个比 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_type std::true_type都有一个名为valuestatic constexpr bool成员变量。分别将这两个模板HasX继承了这个成员变量。(第一个模板继承自std::false_type,第二个模板继承自std::true_type。)
让我们从简单开始,一步步进行直到得到上述代码。
1)起始点:
template <typename T, typename U>
struct HasX : std::false_type { };

在这种情况下,没有什么意外:HasX派生自std :: false_type,因此HasX :: value == false和HasX :: value == false。
2)将U默认为:
// Primary template
template <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 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) 会产生 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.2double。例如,true的类型是booldecltype(true, 0)也意味着int
那么类类型呢?例如,decltype(f, 0)是什么意思?自然会认为这仍然意味着int,但情况可能并非如此。实际上,可能会有一个逗号运算符的重载类似于上面的函数func,它接受fooint并返回char。在这种情况下,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
SFINAE:
这是整个科学;-)好的,我有点夸张,但它也不是很简单。因此,我将把解释保持在最少限度。

SFINAE是Substitution Failure is Not An Error的缩写。这意味着当一个模板参数被一个类型替代时,可能会出现非法的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 替换为 boolT::x 中产生了一个无效的构造。根据 SFINAE 原则,编译器不会拒绝代码,它只是简单地忽略(部分)代码。更准确地说,如我们所见,HasX<bool> 实际上意味着 HasX<bool, int>。应选择 U = int 的特化,但在实例化时,编译器发现了 bool::x 并完全忽略了模板特化,就好像它不存在一样。
此时,代码与仅存在主模板的情况(2)基本相同。因此,HasX<bool, int>::value == false
对于 B,与 bool 相同的论点成立,因为 B::x 是一个无效的构造(B 没有成员 x)。但是,A::x 是可以的,编译器在实例化 U = int 的特化时没有问题(或者更准确地说,实例化为 U = decltype((void) A::x, 0))。因此,HasX<A>::value == true
6)取消命名U
好吧,再次查看(5)中的代码,我们发现名称U在其声明中以外的任何地方都没有使用。然后我们可以取消第二个模板参数的命名,从而得到本帖子顶部显示的代码。

5
@DarioP:我已经添加了一份说明更新。正如您所见,说明非常详细,因为正确的细节数量始终取决于读者的知识水平。为了初学者的利益,我假设了很少的先验知识。我希望这很清晰但不会令人厌烦。 - Cassio Neri
1
谢谢,如果我能的话,我会再次点赞第二次!这太棒了! - DarioP
2
这是我见过的最干净、最简单的实现。对于GCC的问题,我通过将默认模板参数void而不是int,然后删除decltype中的逗号运算符部分来解决了它们。- http://ideone.com/QZHfai - wakjah
1
以下文章解释了如何检测是否存在成员变量,即使它是私有的。它基于上面的解决方案 https://dev59.com/F73pa4cB1Zd3GeqPfX96?noredirect=1#comment113421398_64139547 - User12547645
1
@tartaruga_casco_mole 是的,你的理解是正确的。我选择使用 int0 只是为了节省打字时间 :-) 正如 @wakjah 在他们的评论中指出的那样,这可以进一步简化,通过使用 void 而不是 intbool - Cassio Neri
显示剩余7条评论

54

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


3
好的,请稍等:yeah,屏住呼吸:http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1(由一个人在您提供的SO帖子中链接)。如果您不能立即理解那个人的代码是做什么的,不必担心。它非常聪明,我也花了很长时间才理解。 - Johannes Schaub - litb
它的“has_member”也在执行我的代码所做的事情 :) 它首先使用这个,因为只有这样,“using type::operator();”才不会引发编译错误(然后保证存在“operator()”)。 - Johannes Schaub - litb
1
我在我的博客上发了一篇关于这个的文章,希望你们不介意 :) 链接:http://cpptalk.wordpress.com/2009/09/11/substitution-failure-is-not-an-error-1/ 很有趣的阅读。 - rmn
1
@rmn,它不会检查integer成员,但它会检查任何名为x的数据或函数成员,其类型是任意的。引入成员名称的唯一目的是为了可能存在成员名称查找的歧义-成员的类型并不重要。 - Johannes Schaub - litb
2
现在这段代码的完整解释已经发布在以下网址上:http://cpptalk.wordpress.com/2009/09/12/substitution-failure-is-not-an-error-2/。litb,欢迎您验证我是否正确翻译了它 :) - rmn
显示剩余10条评论

35

我从一个问题中被重定向到这里,该问题已被关闭为此问题的重复。我知道这是一个旧帖子,但我想建议一个替代(更简单?)的实现方法,它可以与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;
}

一个不错的解决方案,但如果X有一个私有成员变量id,你会遇到问题。 - Xoph
2
我认为你根本不需要使用declval,在我的测试中,decltype(T::id, void())可以很好地工作。 - LB--

11

我们可以使用C++20的requires表达式来解决这个问题。感谢@lefticus最近在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;
}
你可以在这里实时查看

7

更新:我最近对我在原始答案中发布的代码进行了一些修改,因此我正在更新这个内容以适应更改/添加。

以下是一些使用片段: *所有这些的关键部分都在下面

检查给定类中的成员x。可以是变量、函数、类、联合或枚举:

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

宏 (魔鬼!):

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)

可变参数模板在当前的C++标准中无效。 - Kirill V. Lyadvinsky
也许需要指出的是,这对于继承方法不起作用...但也许这是有意的。我猜std::integral_constant在这里是限制因素。 - TomSmartBishop

4
Boost.ConceptTraits提供了一些宏来定义类型特征,例如BOOST_TT_EXT_DEFINE_HAS_MEMBER(name),它定义了一个形式为的类型特征:其中包括其他内容:

Boost.ConceptTraits

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一起是处理大多数概念的完美替代品。

1
Concept Traits不再维护,因为该函数演变成以下两个功能: http://www.boost.org/doc/libs/1_42_0/libs/mpl/doc/refmanual/introspection.html 其他功能转移到了Concepts_checks中,例如: http://www.boost.org/doc/libs/1_42_0/libs/concept_check/reference.htm#iterator-archetype - Blaisorblade
你是对的。has_xxx已经在Boost中并且回答了这个问题。我不同意第二个链接,因为原型和概念检查是同一硬币的两面。 - Vicente Botet Escriba

2
为什么不像这样使用专业知识:

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

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

因为我实际上不知道结构体P2包含大写X,而P1包含小写x。这些结构体只是为了举例。可能会有任何结构体或类。 - Kirill V. Lyadvinsky
那么我不认为有任何方法可以使用模板来识别它(也许我是错的)。如果x的数据类型在P1和P2中不同,那么也许我们可以使用sizeof来返回true或false。 - Naveen
在我的问题中有一种方法(实际上有两种不同的方法)。但我不知道如何在两个编译器中识别它。 - Kirill V. Lyadvinsky

1

函数 (x、X、y、Y) 是否来自抽象基类,或者它们可以重构为抽象基类?如果是这样,您可以使用 Modern C++ Design 中的 SUPERSUBCLASS() 宏,以及此问题答案中的思路:

基于编译时类型的分派


0
我们可以在编译时获取每个所需类和成员 - 对象或函数的0-非成员,1-是对象,2-是函数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() {}
};

0
为什么不创建 Check_x 的模板特化呢?
template<> bool Check_x(P1 p) { return true; }
template<> bool Check_x(P2 p) { return false; }

天哪,我想了想。如果你只有两种类型,为什么还需要模板呢?

有不止两种类型。请看我对Naveen回答的评论。 - Kirill V. Lyadvinsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接