关于c ++:检查一个类是否具有给定签名的成员函数

关于c ++:检查一个类是否具有给定签名的成员函数

Check if a class has a member function of a given signature

我要求使用模板技巧来检测类是否具有给定签名的特定成员函数。

这个问题与这里引用的问题类似网址:http://www.gotw.ca/gotw/071.htm但并非如此:在萨特的书中,他回答了一个问题,即C类必须提供一个具有特定签名的成员函数,否则程序将无法编译。在我的问题中,如果一个类有这个函数,我需要做一些事情,否则就做"其他事情"。

Boost::Serialization也遇到了类似的问题,但我不喜欢他们采用的解决方案:一个模板函数,它默认调用一个带有特定签名的自由函数(必须定义),除非您定义了一个带有特定签名的特定成员函数(在这种情况下,"Serialization"接受两个给定类型的参数)。否则会发生编译错误。即实现侵入式和非侵入式序列化。

我不喜欢这个解决方案有两个原因:

  • 为了不受干扰,您必须重写boost::serialization命名空间中的全局"serialization"函数,以便在客户端代码中打开命名空间boost和命名空间序列化!
  • 解决这个问题的堆栈mess是10到12个函数调用。
  • 我需要为没有该成员函数的类定义自定义行为,并且我的实体在不同的命名空间中(我不想在另一个命名空间中重写在一个命名空间中定义的全局函数)

    你能给我一个提示来解决这个难题吗?


    这是一个依赖C++ 11特性的可能实现。它可以正确地检测函数,即使它是继承的(不像迈克·金汉在他的答案中观察到的,接受的答案中的解决方案)。

    此代码段测试的函数称为serialize

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #include <type_traits>

    // Primary template with a static assertion
    // for a meaningful error message
    // if it ever gets instantiated.
    // We could leave it undefined if we didn't care.

    template<typename, typename T>
    struct has_serialize {
        static_assert(
            std::integral_constant<T, false>::value,
           "Second template parameter needs to be of function type.");
    };

    // specialization that does the checking

    template<typename C, typename Ret, typename... Args>
    struct has_serialize<C, Ret(Args...)> {
    private:
        template<typename T>
        static constexpr auto check(T*)
        -> typename
            std::is_same<
                decltype( std::declval<T>().serialize( std::declval<Args>()... ) ),
                Ret    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            >::type;  // attempt to call it and see if the return type is correct

        template<typename>
        static constexpr std::false_type check(...);

        typedef decltype(check<C>(0)) type;

    public:
        static constexpr bool value = type::value;
    };

    用法:

    1
    2
    3
    4
    5
    6
    7
    struct X {
         int serialize(const std::string&) { return 42; }
    };

    struct Y : X {};

    std::cout << has_serialize<Y, int(const std::string&)>::value; // will print 1

    我不确定我是否正确理解您,但是您可以利用sfinae在编译时检测函数是否存在。代码中的示例(测试类是否具有成员函数大小u t used u memory()const)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    template<typename T>
    struct HasUsedMemoryMethod
    {
        template<typename U, size_t (U::*)() const> struct SFINAE {};
        template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
        template<typename U> static int Test(...);
        static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
    };

    template<typename TMap>
    void ReportMemUsage(const TMap& m, std::true_type)
    {
            // We may call used_memory() on m here.
    }
    template<typename TMap>
    void ReportMemUsage(const TMap&, std::false_type)
    {
    }
    template<typename TMap>
    void ReportMemUsage(const TMap& m)
    {
        ReportMemUsage(m,
            std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
    }


    对compileTime成员函数这个问题的公认答案反省虽然很受欢迎,但有一个可以观察到的障碍在以下程序中:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #include <type_traits>
    #include <iostream>
    #include <memory>

    /*  Here we apply the accepted answer's technique to probe for the
        the existence of `E T::operator*() const`
    */

    template<typename T, typename E>
    struct has_const_reference_op
    {
        template<typename U, E (U::*)() const> struct SFINAE {};
        template<typename U> static char Test(SFINAE<U, &U::operator*>*);
        template<typename U> static int Test(...);
        static const bool value = sizeof(Test<T>(0)) == sizeof(char);
    };

    using namespace std;

    /* Here we test the `std::` smart pointer templates, including the
        deprecated `auto_ptr<T>`, to determine in each case whether
        T = (the template instantiated for `int`) provides
        `int & T::operator*() const` - which all of them in fact do.
    */

    int main(void)
    {
        cout << has_const_reference_op,int &>::value;
        cout << has_const_reference_op<unique_ptr<int>,int &>::value;
        cout << has_const_reference_op<shared_ptr<int>,int &>::value << endl;
        return 0;
    }

    用GCC4.6.3构建,程序输出110——通知我们T = std::shared_ptr不提供int & T::operator*() const。好的。

    如果你还不明白这一点,那就看看收割台中的std::shared_ptr将发光。在那实现,std::shared_ptr是从基类派生的它继承了以东十一〔六〕年。所以模板实例化SFINAE构成"查找"运算符的U = std::shared_ptr不会发生,因为std::shared_ptr没有operator*()本身的权利和模板实例化没有"继承"。好的。

    这个障碍不影响著名的sfinae方法,使用"sizeof()技巧",仅用于检测T是否具有某些成员功能mf(参见这个答案和评论)。但是确定T::mf的存在往往是(通常是?)不够好:你可以还需要确定它具有所需的签名。这就是图示技术得分。所需签名的指针化变量在必须满足的模板类型的参数中&T::mf为斯芬纳探测器成功。但是这个模板正在实例化当T::mf被继承时,技术给出了错误的答案。好的。

    一种用于T::mf编译时自省的安全sfinae技术必须避免在模板参数中使用&T::mf来实例化一个类型,在该类型上函数模板分辨率取决于。相反,sfinae模板函数解析只能依赖于使用的完全相关的类型声明作为重载sfinae探测函数的参数类型。好的。

    为了回答这个约束条件下的问题,我将说明E T::operator*() const的编译检测,用于任意TE。同样的模式将在必要的修改后适用。探测任何其他成员方法签名。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #include <type_traits>

    /*! The template `has_const_reference_op<T,E>` exports a
        boolean constant `value that is true iff `T` provides
        `E T::operator*() const`
    */

    template< typename T, typename E>
    struct has_const_reference_op
    {
        /* SFINAE operator-has-correct-sig :) */
        template<typename A>
        static std::true_type test(E (A::*)() const) {
            return std::true_type();
        }

        /* SFINAE operator-exists :) */
        template <typename A>
        static decltype(test(&A::operator*))
        test(decltype(&A::operator*),void *) {
            /* Operator exists. What about sig? */
            typedef decltype(test(&A::operator*)) return_type;
            return return_type();
        }

        /* SFINAE game over :( */
        template<typename A>
        static std::false_type test(...) {
            return std::false_type();
        }

        /* This will be either `std::true_type` or `std::false_type` */
        typedef decltype(test<T>(0,0)) type;

        static const bool value = type::value; /* Which is it? */
    };

    在这个解决方案中,重载的sfinae探测函数test()被"调用"递归地(当然,它根本没有被调用;它只是编译器解决的假设调用的返回类型。)好的。

    我们需要调查至少一个和最多两个信息点:好的。

    • T::operator*()是否存在?如果没有,我们就完了。
    • 鉴于T::operator*()存在,其签名是EDOCX1?18?

    我们通过评估单个呼叫的返回类型得到答案。至test(0,0)。这是通过:好的。

    1
        typedef decltype(test<T>(0,0)) type;

    此调用可能会解决/* SFINAE operator-exists :) */过载问题。或者可以解决/* SFINAE game over :( */的过载问题。它不能解决/* SFINAE operator-has-correct-sig :) */过载问题,因为这只需要一个论点,而我们要传递两个。好的。

    我们为什么要过两个?只是为了迫使决议排除/* SFINAE operator-has-correct-sig :) */。第二个论点没有其他意义。好的。

    test(0,0)的调用将解决/* SFINAE operator-exists :) */的问题。如果第一个参数0满足该重载的第一个参数类型,它是decltype(&A::operator*),和A = T。0将满足该类型以防万一。好的。

    让我们假设编译器对此表示同意。那它就跟/* SFINAE operator-exists :) */需要确定返回类型函数调用,在这种情况下是decltype(test(&A::operator*))。-对test()的另一个调用的返回类型。好的。

    这次,我们只传递一个论点,&A::operator*,我们现在知道存在,否则我们就不会在这里。打电话给test(&A::operator*)可能向/* SFINAE operator-has-correct-sig :) */或再次决议可能会解决/* SFINAE game over :( */。电话会匹配的以防万一以防万一该过载的单参数类型,即E (A::*)() const,与A = T一起。好的。

    如果T::operator*具有所需的签名,编译器将在这里说是,然后再次评估重载的返回类型。不再"递归"现在:它是std::true_type。好的。

    如果编译器没有为调用test(0,0)或不选择/* SFINAE operator-has-correct-sig :) */。对于调用test(&A::operator*),则在任何情况下,它都与/* SFINAE game over :( */,最终返回类型为std::false_type。好的。

    下面是一个测试程序,显示生成预期不同案例样本的答案(GCC第4.6.3条)。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    // To test
    struct empty{};

    // To test
    struct int_ref
    {
        int & operator*() const {
            return *_pint;
        }
        int & foo() const {
            return *_pint;
        }
        int * _pint;
    };

    // To test
    struct sub_int_ref : int_ref{};

    // To test
    template<typename E>
    struct ee_ref
    {
        E & operator*() {
            return *_pe;
        }
        E & foo() const {
            return *_pe;
        }
        E * _pe;
    };

    // To test
    struct sub_ee_ref : ee_ref<char>{};

    using namespace std;

    #include <iostream>
    #include <memory>
    #include <vector>

    int main(void)
    {
        cout <<"Expect Yes" << endl;
        cout << has_const_reference_op,int &>::value;
        cout << has_const_reference_op<unique_ptr<int>,int &>::value;
        cout << has_const_reference_op<shared_ptr<int>,int &>::value;
        cout << has_const_reference_op<std::vector<int>::iterator,int &>::value;
        cout << has_const_reference_op<std::vector<int>::const_iterator,
                int const &>::value;
        cout << has_const_reference_op<int_ref,int &>::value;
        cout << has_const_reference_op<sub_int_ref,int &>::value  << endl;
        cout <<"Expect No" << endl;
        cout << has_const_reference_op<int *,int &>::value;
        cout << has_const_reference_op<unique_ptr<int>,char &>::value;
        cout << has_const_reference_op<unique_ptr<int>,int const &>::value;
        cout << has_const_reference_op<unique_ptr<int>,int>::value;
        cout << has_const_reference_op<unique_ptr<long>,int &>::value;
        cout << has_const_reference_op<int,int>::value;
        cout << has_const_reference_op<std::vector<int>,int &>::value;
        cout << has_const_reference_op<ee_ref<int>,int &>::value;
        cout << has_const_reference_op<sub_ee_ref,int &>::value;
        cout << has_const_reference_op::value  << endl;
        return 0;
    }

    这个想法有新的缺陷吗?它能再普通一次吗与它避免的障碍物相撞?好的。好啊。


    以下是一些用法片段:*所有这些的胆量都在进一步下降。

    检查给定类中的成员x。可以是var、func、class、union或enum:

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

    检查成员函数void x()

    1
    2
    3
    //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

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

    检查成员类x

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

    检查是否有会员工会x

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

    检查成员枚举x

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

    检查是否有任何成员函数x,不考虑签名:

    1
    2
    3
    4
    5
    6
    7
    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;

    1
    2
    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;

    细节和核心:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    /*
        - 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.
    */


    //Variadic to force ambiguity of class members.  C++11 and up.
    template <typename... Args> struct ambiguate : public Args... {};

    //Non-variadic version of the line above.
    //template <typename A, typename B> struct ambiguate : public A, public B {};

    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!):

    创建成员检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //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>            \
                , Alias_##member<AmbiguitySeed_##member>                        \
            >::value                                                            \
        ;                                                                       \
    }

    创建成员变量检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //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 {}

    创建成员检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //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 {}

    创建u成员u类u检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //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 {}

    创建成员联合检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //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 {}

    创建成员枚举检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //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 {}

    创建成员检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //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    \
        ;                                           \
    }

    创建成员检查:

    1
    2
    3
    4
    5
    6
    7
    8
    //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)

    如果您知道所期望的成员函数的名称,这就足够了。(在这种情况下,如果没有成员函数,那么函数bla将无法实例化(编写一个无论如何都有效的函数是很困难的,因为缺少函数部分专门化)。您可能还需要使用类模板)另外,启用结构(类似于启用_if)也可以在您希望它作为成员的函数类型上进行模板化。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename T, int (T::*) ()> struct enable { typedef T type; };
    template <typename T> typename enable<T, &T::i>::type bla (T&);
    struct A { void i(); };
    struct B { int i(); };
    int main()
    {
      A a;
      B b;
      bla(b);
      bla(a);
    }


    下面是对迈克·金汉的回答的简单理解。这将检测继承的方法。它还将检查准确的签名(不像jrok允许参数转换的方法)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    template <class C>
    class HasGreetMethod
    {
        template <class T>
        static std::true_type testSignature(void (T::*)(const char*) const);

        template <class T>
        static decltype(testSignature(&T::greet)) test(std::nullptr_t);

        template <class T>
        static std::false_type test(...);

    public:
        using type = decltype(test<C>(nullptr));
        static const bool value = type::value;
    };

    struct A { void greet(const char* name) const; };
    struct Derived : A { };
    static_assert(HasGreetMethod<Derived>::value,"");

    可运行示例


    要实现这一点,我们需要使用:

  • 根据方法是否可用,使用不同返回类型的函数模板重载
  • 为了与type_traits头中的元条件保持一致,我们希望从重载返回true_typefalse_type
  • 声明true_type重载,期望intfalse_type重载,期望使用可变参数:"重载分辨率中省略号转换的最低优先级"
  • 在定义true_type函数的模板规范时,我们将使用declvaldecltype来检测函数,而不依赖于方法之间的返回类型差异或重载。
  • 你可以在这里看到一个活生生的例子。但我也会在下面解释:

    我想检查是否存在一个名为test的函数,它采用可从int转换的类型,然后我需要声明这两个函数:

    1
    2
    template <typename T, typename S = decltype(declval<T>().test(declval<int>))> static true_type hasTest(int);
    template <typename T> static false_type hasTest(...);
    • decltype(hasTest(0))::valuetrue(注:不需要创建特殊功能来处理void a::test()过载,接受void a::test(int))
    • decltype(hasTest(0))::valuetrue(因为int可转换为doubleint b::test(double)可接受,与返回类型无关)
    • decltype(hasTest(0))::valuefalse(c没有名为test的方法接受可从int转换的类型,因此不接受)

    此解决方案有两个缺点:

  • 需要一对函数的每方法声明
  • 创建名称空间污染,特别是如果我们要测试相似的名称,例如,我们将如何命名一个要测试test()方法的函数?
  • 因此,这些函数必须在一个详细的名称空间中声明,或者理想情况下,如果它们只与一个类一起使用,那么它们应该由该类私下声明。为此,我编写了一个宏来帮助您抽象这些信息:

    1
    2
    3
    #define FOO(FUNCTION, DEFINE) template <typename T, typename S = decltype(declval<T>().FUNCTION)> static true_type __ ## DEFINE(int); \
                                  template <typename T> static false_type __ ## DEFINE(...); \
                                  template <typename T> using DEFINE = decltype(__ ## DEFINE<T>(0));

    您可以这样使用:

    1
    2
    3
    4
    namespace details {
        FOO(test(declval<int>()), test_int)
        FOO(test(), test_void)
    }

    随后调用details::test_int::valuedetails::test_void::value将产生用于内联代码或元编程的truefalse


    我自己也有同样的问题,发现这里提出的解决方案很有趣…但需要一个解决方案:

    百万千克1检测继承的函数;百万千克1百万千克1与非C++ 11就绪编译器兼容(所以没有解密)百万千克1

    基于一个提振讨论,找到了另一个类似这样的建议。下面是根据boost::has_ux类的模型,将建议的解决方案概括为两个traits类的宏声明。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    #include <boost/type_traits/is_class.hpp>
    #include <boost/mpl/vector.hpp>

    /// Has constant function
    /** \param func_ret_type Function return type
        \param func_name Function name
        \param ... Variadic arguments are for the function parameters
    */

    #define DECLARE_TRAITS_HAS_FUNC_C(func_ret_type, func_name, ...) \
        __DECLARE_TRAITS_HAS_FUNC(1, func_ret_type, func_name, ##__VA_ARGS__)


    /// Has non-const function
    /** \param func_ret_type Function return type
        \param func_name Function name
        \param ... Variadic arguments are for the function parameters
    */

    #define DECLARE_TRAITS_HAS_FUNC(func_ret_type, func_name, ...) \
        __DECLARE_TRAITS_HAS_FUNC(0, func_ret_type, func_name, ##__VA_ARGS__)


    // Traits content
    #define __DECLARE_TRAITS_HAS_FUNC(func_const, func_ret_type, func_name, ...)  \
        template                                                                  \
        <   typename Type,                                                        \
            bool is_class = boost::is_class<Type>::value                          \
        >                                                                         \
        class has_func_ ## func_name;                                             \
        template<typename Type>                                                   \
        class has_func_ ## func_name<Type,false>                                  \
        {public:                                                                  \
            BOOST_STATIC_CONSTANT( bool, value = false );                         \
            typedef boost::false_type type;                                       \
        };                                                                        \
        template<typename Type>                                                   \
        class has_func_ ## func_name<Type,true>                                   \
        {   struct yes { char _foo; };                                            \
            struct no { yes _foo[2]; };                                           \
            struct Fallback                                                       \
            {   func_ret_type func_name( __VA_ARGS__ )                            \
                    UTILITY_OPTIONAL(func_const,const) {}                         \
            };                                                                    \
            struct Derived : public Type, public Fallback {};                     \
            template <typename T, T t>  class Helper{};                           \
            template <typename U>                                                 \
            static no deduce(U*, Helper                                           \
                <   func_ret_type (Fallback::*)( __VA_ARGS__ )                    \
                        UTILITY_OPTIONAL(func_const,const),                       \
                    &U::func_name                                                 \
                >* = 0                                                            \
            );                                                                    \
            static yes deduce(...);                                               \
        public:                                                                   \
            BOOST_STATIC_CONSTANT(                                                \
                bool,                                                             \
                value = sizeof(yes)                                               \
                    == sizeof( deduce( static_cast<Derived*>(0) ) )               \
            );                                                                    \
            typedef ::boost::integral_constant<bool,value> type;                  \
            BOOST_STATIC_CONSTANT(bool, is_const = func_const);                   \
            typedef func_ret_type return_type;                                    \
            typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;                \
        }


    // Utility functions
    #define UTILITY_OPTIONAL(condition, ...) UTILITY_INDIRECT_CALL( __UTILITY_OPTIONAL_ ## condition , ##__VA_ARGS__ )
    #define UTILITY_INDIRECT_CALL(macro, ...) macro ( __VA_ARGS__ )
    #define __UTILITY_OPTIONAL_0(...)
    #define __UTILITY_OPTIONAL_1(...) __VA_ARGS__

    这些宏扩展到具有以下原型的traits类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    template<class T>
    class has_func_[func_name]
    {
    public:
        /// Function definition result value
        /** Tells if the tested function is defined for type T or not.
        */

        static const bool value = true | false;

        /// Function definition result type
        /** Type representing the value attribute usable in
            http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html
        */

        typedef boost::integral_constant<bool,value> type;

        /// Tested function constness indicator
        /** Indicates if the tested function is const or not.
            This value is not deduced, it is forced depending
            on the user call to one of the traits generators.
        */

        static const bool is_const = true | false;

        /// Tested function return type
        /** Indicates the return type of the tested function.
            This value is not deduced, it is forced depending
            on the user's arguments to the traits generators.
        */

        typedef func_ret_type return_type;

        /// Tested function arguments types
        /** Indicates the arguments types of the tested function.
            This value is not deduced, it is forced depending
            on the user's arguments to the traits generators.
        */

        typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;
    };

    那么,我们能用它做什么呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    // We enclose the traits class into
    // a namespace to avoid collisions
    namespace ns_0 {
        // Next line will declare the traits class
        // to detect the member function void foo(int,int) const
        DECLARE_TRAITS_HAS_FUNC_C(void, foo, int, int);
    }

    // we can use BOOST to help in using the traits
    #include <boost/utility/enable_if.hpp>

    // Here is a function that is active for types
    // declaring the good member function
    template<typename T> inline
    typename boost::enable_if< ns_0::has_func_foo<T> >::type
    foo_bar(const T &_this_, int a=0, int b=1)
    {   _this_.foo(a,b);
    }

    // Here is a function that is active for types
    // NOT declaring the good member function
    template<typename T> inline
    typename boost::disable_if< ns_0::has_func_foo<T> >::type
    foo_bar(const T &_this_, int a=0, int b=1)
    {   default_foo(_this_,a,b);
    }

    // Let us declare test types
    struct empty
    {
    };
    struct direct_foo
    {
        void foo(int,int);
    };
    struct direct_const_foo
    {
        void foo(int,int) const;
    };
    struct inherited_const_foo :
        public direct_const_foo
    {
    };

    // Now anywhere in your code you can seamlessly use
    // the foo_bar function on any object:
    void test()
    {
        int a;
        foo_bar(a); // calls default_foo

        empty b;
        foo_bar(b); // calls default_foo

        direct_foo c;
        foo_bar(c); // calls default_foo (member function is not const)

        direct_const_foo d;
        foo_bar(d); // calls d.foo (member function is const)

        inherited_const_foo e;
        foo_bar(e); // calls e.foo (inherited member function)
    }


    您可以使用std::is_member_function_pointer

    1
    2
    3
    4
    5
    6
    class A {
       public:
         void foo() {};
    }

     bool test = std::is_member_function_pointer<decltype(&A::foo)>::value;

    为了不受干扰,还可以通过Koenig查找将serialize放在要序列化的类或存档类的命名空间中。有关更多详细信息,请参阅命名空间以获取自由函数重写。:-)

    打开任何给定的名称空间来实现自由函数都是错误的。(例如,您不应该为自己的类型打开命名空间std来实现swap,而是应该使用koenig查找。)


    可以。第二次尝试。如果你也不喜欢这个,没关系,我在找更多的主意。

    Herb Sutter的文章谈到了特性。因此,您可以有一个traits类,它的默认实例化具有回退行为,对于存在成员函数的每个类,traits类都专门用于调用成员函数。我相信Herb的文章提到了这样做的一种技巧,这样就不需要大量的复制和粘贴。

    不过,正如我所说的,也许您不希望在实现该成员的"标记"类中进行额外的工作。在这种情况下,我正在寻找第三种解决方案……


    没有C + +的支持(11 decltypethis might work):P></sscce

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #include <iostream>
    using namespace std;

    struct A { void foo(void); };
    struct Aa: public A { };
    struct B { };

    struct retA { int foo(void); };
    struct argA { void foo(double); };
    struct constA { void foo(void) const; };
    struct varA { int foo; };

    template<typename T>
    struct FooFinder {
        typedef char true_type[1];
        typedef char false_type[2];

        template<int>
        struct TypeSink;

        template<class U>
        static true_type &match(U);

        template<class U>
        static true_type &test(TypeSink<sizeof( matchType<void (U::*)(void)>( &U::foo ) )> *);

        template<class U>
        static false_type &test(...);

        enum { value = (sizeof(test<T>(0, 0)) == sizeof(true_type)) };
    };

    int main() {
        cout << FooFinder<A>::value << endl;
        cout << FooFinder<Aa>::value << endl;
        cout << FooFinder::value << endl;

        cout << FooFinder<retA>::value << endl;
        cout << FooFinder::value << endl;
        cout << FooFinder<constA>::value << endl;
        cout << FooFinder<varA>::value << endl;
    }

    恩hopefully工程知识

    AAaBclases,问题是在那一个特殊的人,Aathe looking for the inherits我们的成员。P></

    茶和茶FooFindertrue_typefalse_typeare the替换for the C++类意味着11。也理解模板元编程for the of the Very他们揭示的基础上,sfinae of the - sizeof戏法。P></

    TypeSinkis the that is used to struct模板库后sizeofthe result of the积分算子模板实例化to form into a A型。P></

    一sfinae matchfunction is the kind of that is left without a模板counterpart GENERIC。因此,它可以被实例化,if only be the type of its type argument for the matches恩是专业。P></

    testboth the the枚举函数在一起最后宣言,中央sfinae形态模式。在一个使用Generic there is that the false_type安ellipsis归来》和counterpart Arguments to take with more特异值优先。P></

    to be to the test阿贝尔函数模板实例化与Targument of the function,matchmust be as its实例化,实例化类来返回TypeSinkargument is the。但是&U::foois that the function argument的幸福中,骨膜,is not to argument from within a,所有成员的专业模板,我需要知道inherited lookup广场。P></


    如果你使用Facebook,他们放开,are out of the box宏帮助你:P></

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <folly/Traits.h>
    namespace {
      FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test);
    } // unnamed-namespace

    void some_func() {
      cout <<"Does class Foo have a member int test() const?"
        << boolalpha << has_test_traits<Foo, int() const>::value;
    }

    虽然is the same the implementation details with the previous答案是简单的内部,使用图书馆。P></


    我相信你要找的答案就在这里。

    http://www.martinecker.com/wiki/index.php?title=在编译时检测u运算符的存在

    这里有一个稍微充实一点的例子

    http://pastie.org/298994

    我使用这项技术来检测相关类上支持的Ostream运算符的存在,然后根据不同的代码位生成不同的代码。

    我不相信在找到关联的解决方案之前是可能的,但这是一个非常巧妙的技巧。花时间理解代码,这是非常值得的。

    布拉德


    推荐阅读

      检查linux服务器命令?

      检查linux服务器命令?,系统,信息,状态,情况,时间,工具,网络,环境,服务,电脑,

      linux命令引用文件名?

      linux命令引用文件名?,工作,系统,信息,命令,数据,文件,时间,灵活,名称,标准,l

      linux磁盘检测命令?

      linux磁盘检测命令?,情况,系统,数据,检测,管理,信息,命令,磁盘,设备,单位,lin

      linux检测gpu命令?

      linux检测gpu命令?,信息,系统,工具,检测,情况,电脑,数字,环境,网上,报告,linu

      硬盘检测命令linux?

      硬盘检测命令linux?,信息,情况,系统,管理,数据,检测,百分比,命令,工具,设备,l

      linux性能检测命令?

      linux性能检测命令?,系统,情况,信息,状态,工具,实时,百分比,指标,分析,命令,

      linux检查硬盘的命令?

      linux检查硬盘的命令?,系统,信息,检测,情况,命令,工具,电脑,地址,设备,硬盘,l

      linux检查挂载命令?

      linux检查挂载命令?,设备,系统,信息,情况,状态,服务,软件,命令,磁盘,网络,lin

      linux检测硬件的命令?

      linux检测硬件的命令?,信息,系统,检测,工具,第一,数据,设备,分析,实时,百度,

      linux一般检查命令?

      linux一般检查命令?,网络,系统,检测,情况,工作,信息,命令,进程,时间,设备,lin

      检查硬件linux命令?

      检查硬件linux命令?,信息,系统,第一,数据,设备,检测,命令,情况,灵活,实时,如

      linux命令超时检测?

      linux命令超时检测?,时间,网络,检测,系统,地址,状态,电脑,代码,软件,设备,lin

      linux访问模板命令?

      linux访问模板命令?,工作,地址,系统,信息,管理,命令,目录,发行,设备,文件,lin

      检查路由命令linux?

      检查路由命令linux?,网络,地址,系统,信息,工具,电脑,时间,通信,服务,命令,lin

      linux下端口检测命令?

      linux下端口检测命令?,检测,系统,状态,工具,情况,端口,网络,服务,灵活,信息,l

      linux数据库检查命令?

      linux数据库检查命令?,服务,状态,地址,位置,系统,信息,命令,工作,情况,密码,

      linux分区检查命令是?

      linux分区检查命令是?,系统,设备,工具,管理,情况,信息,检测,分区,密码,单位,

      linux检测摄像头命令?

      linux检测摄像头命令?,系统,工具,情况,实时,信息,状态,平均,设备,检测,数据,L

      linux检测进程命令?

      linux检测进程命令?,系统,服务,地址,状态,信息,检测,进程,命令,第一,软件,lin

      linux检查流量的命令?

      linux检查流量的命令?,工具,系统,实时,状态,网络,信息,数据,密码,地址,流量,l