关于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运算符的存在,然后根据不同的代码位生成不同的代码。

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

    布拉德


    推荐阅读