注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

SFINAE原理及其在模板中的使用之四各种类型检测(转载)  

2014-07-04 15:11:56|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

SFINAE原理及其在模板中的使用之四各种类型检测(转载)

http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions

SFINAE to check for inherited member functions

 

up vote

20

down vote

favorite

26

Using SFINAE, i can detect wether a given class has a certain member function. But what if i want to test for inherited member functions?

 

The following does not work in VC8 and GCC4 (i.e. detects that A has a member function foo(), but not that B inherits one):

 

#include <iostream>

 

template<typename T, typename Sig>                                

struct has_foo {                    

    template <typename U, U> struct type_check;

    template <typename V> static char (& chk(type_check<Sig, &V::foo>*))[1];

    template <typename  > static char (& chk(...))[2];

    static bool const value = (sizeof(chk<T>(0)) == 1);

};

 

struct A {

    void foo();

};

 

struct B : A {};

 

int main()

{

    using namespace std;

    cout << boolalpha << has_foo<A, void (A::*)()>::value << endl; // true

    cout << boolalpha << has_foo<B, void (B::*)()>::value << endl; // false

}

So, is there a way to test for inherited member functions?

 

c++ templates metaprogramming sfinae

share|improve this question

asked Dec 27 '09 at 16:45

 

Georg Fritzsche

58.1k8119157

             

I don't think this is possible in general. So far, all ways i've seen have some cases where they fail. –  Johannes Schaub - litb Dec 28 '09 at 15:59

add comment

3 Answers

activeoldestvotes

up vote

21

down vote

accepted

Take a look at this thread:

 

http://lists.boost.org/boost-users/2009/01/44538.php

 

Derived from the code linked to in that discussion:

 

#include <iostream>

 

template <typename Type>

class has_foo

{

   class yes { char m;};

   class no { yes m[2];};

   struct BaseMixin

   {

     void foo(){}

   };

   struct Base : public Type, public BaseMixin {};

   template <typename T, T t>  class Helper{};

   template <typename U>

   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 0);

   static yes deduce(...);

public:

   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));

};

 

struct A {

    void foo();

};

 

struct B : A {};

 

struct C {};

 

int main()

{

    using namespace std;

    cout << boolalpha << has_foo<A>::result << endl;

    cout << boolalpha << has_foo<B>::result << endl;

    cout << boolalpha << has_foo<C>::result;

}

Result:

 

true

true

false

share|improve this answer

edited Dec 27 '09 at 22:29

 

answered Dec 27 '09 at 22:10

 

joshperry

17.4k34371

             

Nice, thank you. –  Georg Fritzsche Dec 28 '09 at 8:28

3             

It's the one shown here: stackoverflow.com/questions/1005476/… and explained here: cplusplus.co.il/2009/09/11/… . Keep in mind it will signal that there is a member of that name even if the member isn't a function or the function has some other signature, so it's doing something different. It needs the other codes shown in the usenet discussion to do further checks. –  Johannes Schaub - litb Dec 28 '09 at 15:44

             

Didn't notice the missing type check, good point. You probably mean using is_call_possible<> from here groups.google.com/group/comp.lang.c++.moderated/msg/… ? Looks interesting, will have to test later. –  Georg Fritzsche Dec 29 '09 at 10:17

1             

This code fails to compile with struct Cpp11 final : A {}; because it tries to inherit from a final class. Any workaround? –  Marc Mutz - mmutz Mar 2 '12 at 8:42

             

@mmutz: see my post below as a possible solution –  kispaljr Jan 7 '13 at 15:09

add comment

 

up vote

16

down vote

joshperry's answer is very clever and elegant, but (as it is stated below the post) it doesn't check the signature of foo() properly and doesn't work with fundamental types (like int): it causes a compiler error. I will propose a technique that handles inherited members correctly and also checks the signature of the member function. Instead of going into details I will give you two exampes and hope that the code will speak for itself.

 

Example1:

 

We are checking for a member with the following signature:  T::const_iterator begin() const

 

template<class T> struct has_const_begin

{

    typedef char (&Yes)[1];

    typedef char (&No)[2];

 

    template<class U>

    static Yes test(U const * data,

                    typename std::enable_if<std::is_same<

                             typename U::const_iterator,

                             decltype(data->begin())

                    >::value>::type * = 0);

    static No test(...);

    static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0));

};

Please notice that it even checks the constness of the method, and works with primitive types, as well. (I mean has_const_begin<int>::value is false and doesn't cause a compile-time error.)

 

Example 2

 

Now we are looking for the signature: void foo(MyClass&, unsigned)

 

template<class T> struct has_foo

{

    typedef char (&Yes)[1];

    typedef char (&No)[2];

 

    template<class U>

    static Yes test(U * data, MyClass* arg1 = 0,

                    typename std::enable_if<std::is_void<

                             decltype(data->foo(*arg1, 1u))

                    >::value>::type * = 0);

    static No test(...);

    static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0));

};

Please notice that MyClass doesn't has to be default constructible or to satisfy any special concept. The technique works with template members, as well.

 

I am eagerly waiting opinions regarding this.

 

share|improve this answer

edited Jan 11 '12 at 13:10

 

answered Jan 6 '12 at 3:22

 

kispaljr

506414

             

+1 I was looking for a solution for a problem of mine, and this is exactly what I needed :) –  Tom Knapen Jan 27 '12 at 21:06

             

I can't for the life of me tell if this class (or something equivalent) is currently in Boost... Do you know? –  Dan Nissenbaum Mar 29 '13 at 15:16

             

No, I don't think so. –  kispaljr May 27 '13 at 14:42

add comment

up vote

12

down vote

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

 

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

 

CREATE_MEMBER_CHECK(x);

bool has_x = has_member_x<class_to_check_for_x>::value;

Check for member function void x():

 

//Func signature MUST have T as template variable here... simpler this way :\

CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);

bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

Check for member variable x:

 

CREATE_MEMBER_VAR_CHECK(x);

bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

Check for member class x:

 

CREATE_MEMBER_CLASS_CHECK(x);

bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

Check for member union x:

 

CREATE_MEMBER_UNION_CHECK(x);

bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

Check for member enum x:

 

CREATE_MEMBER_ENUM_CHECK(x);

bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

Check for any member function x regardless of signature:

 

CREATE_MEMBER_CHECK(x);

CREATE_MEMBER_VAR_CHECK(x);

CREATE_MEMBER_CLASS_CHECK(x);

CREATE_MEMBER_UNION_CHECK(x);

CREATE_MEMBER_ENUM_CHECK(x);

CREATE_MEMBER_FUNC_CHECK(x);

bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

OR

 

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.

bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

Details and core:

 

/*

    - Multiple inheritance forces ambiguity of member names.

    - SFINAE is used to make aliases to member names.

    - Expression SFINAE is used in just one generic has_member that can accept

      any alias we pass it.

*/

 

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

};

Macros (El Diablo!):

 

CREATE_MEMBER_CHECK:

 

//Check for any member with given name, whether var, func, class, union, enum.

#define CREATE_MEMBER_CHECK(member)                                         \

                                                                            \

template<typename T, typename = std::true_type>                             \

struct Alias_##member;                                                      \

                                                                            \

template<typename T>                                                        \

struct Alias_##member <                                                     \

    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \

> { static const decltype(&T::member) value; };                             \

                                                                            \

struct AmbiguitySeed_##member { char member; };                             \

                                                                            \

template<typename T>                                                        \

struct has_member_##member {                                                \

    static const bool value                                                 \

        = has_member<                                                       \

            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \

            , Alias_##member<AmbiguitySeed_##member>                        \

        >::value                                                            \

    ;                                                                       \

}

CREATE_MEMBER_VAR_CHECK:

 

//Check for member variable with given name.

#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \

                                                                            \

template<typename T, typename = std::true_type>                             \

struct has_member_var_##var_name : std::false_type {};                      \

                                                                            \

template<typename T>                                                        \

struct has_member_var_##var_name<                                           \

    T                                                                       \

    , std::integral_constant<                                               \

        bool                                                                \

        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \

    >                                                                       \

> : std::true_type {}

CREATE_MEMBER_FUNC_SIG_CHECK:

 

//Check for member function with given name AND signature.

#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \

                                                                            \

template<typename T, typename = std::true_type>                             \

struct has_member_func_##templ_postfix : std::false_type {};                \

                                                                            \

template<typename T>                                                        \

struct has_member_func_##templ_postfix<                                     \

    T, std::integral_constant<                                              \

        bool                                                                \

        , sig_check<func_sig, &T::func_name>::value                         \

    >                                                                       \

> : std::true_type {}

CREATE_MEMBER_CLASS_CHECK:

 

//Check for member class with given name.

#define CREATE_MEMBER_CLASS_CHECK(class_name)               \

                                                            \

template<typename T, typename = std::true_type>             \

struct has_member_class_##class_name : std::false_type {};  \

                                                            \

template<typename T>                                        \

struct has_member_class_##class_name<                       \

    T                                                       \

    , std::integral_constant<                               \

        bool                                                \

        , std::is_class<                                    \

            typename got_type<typename T::class_name>::type \

        >::value                                            \

    >                                                       \

> : std::true_type {}

CREATE_MEMBER_UNION_CHECK:

 

//Check for member union with given name.

#define CREATE_MEMBER_UNION_CHECK(union_name)               \

                                                            \

template<typename T, typename = std::true_type>             \

struct has_member_union_##union_name : std::false_type {};  \

                                                            \

template<typename T>                                        \

struct has_member_union_##union_name<                       \

    T                                                       \

    , std::integral_constant<                               \

        bool                                                \

        , std::is_union<                                    \

            typename got_type<typename T::union_name>::type \

        >::value                                            \

    >                                                       \

> : std::true_type {}

CREATE_MEMBER_ENUM_CHECK:

 

//Check for member enum with given name.

#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \

                                                            \

template<typename T, typename = std::true_type>             \

struct has_member_enum_##enum_name : std::false_type {};    \

                                                            \

template<typename T>                                        \

struct has_member_enum_##enum_name<                         \

    T                                                       \

    , std::integral_constant<                               \

        bool                                                \

        , std::is_enum<                                     \

            typename got_type<typename T::enum_name>::type  \

        >::value                                            \

    >                                                       \

> : std::true_type {}

CREATE_MEMBER_FUNC_CHECK:

 

//Check for function with given name, any signature.

#define CREATE_MEMBER_FUNC_CHECK(func)          \

template<typename T>                            \

struct has_member_func_##func {                 \

    static const bool value                     \

        = has_member_##func<T>::value           \

        && !has_member_var_##func<T>::value     \

        && !has_member_class_##func<T>::value   \

        && !has_member_union_##func<T>::value   \

        && !has_member_enum_##func<T>::value    \

    ;                                           \

}

CREATE_MEMBER_CHECKS:

 

//Create all the checks for one member.  Does NOT include func sig checks.

#define CREATE_MEMBER_CHECKS(member)    \

CREATE_MEMBER_CHECK(member);            \

CREATE_MEMBER_VAR_CHECK(member);        \

CREATE_MEMBER_CLASS_CHECK(member);      \

CREATE_MEMBER_UNION_CHECK(member);      \

CREATE_MEMBER_ENUM_CHECK(member);       \

CREATE_MEMBER_FUNC_CHECK(member)

 

 

  评论这张
 
阅读(976)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017