Type trait: Check if class have specific function (maybe inherit)

前端 未结 3 1064
心在旅途
心在旅途 2021-01-05 07:49

I know that there are many possible ways to detect if a class has a specific function but non of them really work for my exact case. My current implementation to check for t

相关标签:
3条回答
  • 2021-01-05 08:29

    Here is one old school C++03 way of doing it. Typically it can be used as a utility and get it molded for any method or variable.

    #define HasMember(NAME) \
      template<class Class, typename Type = void> \
      struct HasMember_##NAME \
      { \
        typedef char (&yes)[2]; \
        template<unsigned long> struct exists; \
        template<typename V> static yes Check (exists<sizeof(static_cast<Type>(&V::NAME))>*); \
        template<typename> static char Check (...); \
        static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
      }; \
      template<class Class> \
      struct HasMember_##NAME<Class, void> \
      { \
        typedef char (&yes)[2]; \
        template<unsigned long> struct exists; \
        template<typename V> static yes Check (exists<sizeof(&V::NAME)>*); \
        template<typename> static char Check (...); \
        static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
      }
    

    Instantiate:

    HasMember(Foo);
    

    Usage:

    HasMember_Foo<B>::value  // without type (but then no overload allowed)
    HasMember_Foo<C, int (C::*)(float)>::value  // needs type
    

    Note that, here I am providing two HasMember_Foos, 1 with type and 1 without type. They are generalized for any type (not just specific to int (X::*)(float)). If there is no type mentioned, then the class must have only 1 such method (without overload). Hence, it's always safer to mention the type; As you have done in your question, the specific type is int (X::*)(float). BTW, this also can be included using another macro.
    Without such extra macro, in case of class C and class D, you may have to specify the type of the method.

    Here is a demo with your code.


    Here it's assumed that whichever class member (function or variable) is chosen, must be public scoped. i.e. If X::foo is private then this solution will not work.

    0 讨论(0)
  • 2021-01-05 08:32

    Here is a way to do it (work for your 4 test cases, did not test it intensively though), thanks @Jarod42 for the improvement (see initial answer at the end):

    template <typename T>
    int call_foo (int (T::*)(float));
    
    template <typename C>
    std::true_type has_foo(decltype(call_foo(&C::foo)));
    
    template <typename C>
    std::false_type has_foo (...);    
    
    template<typename T>
    using HasFoo = decltype(has_foo<T>(0));
    

    The problem with your code was that you were expecting U::* whereas &B::foo is A::* (not B::*). Here I let the compiler choose the value of T by using implicit type deduction so I don't run into such issue.

    The code works as follow:

    • If T does not have a foo member, then the compiler will choose the second overload of has_foo.
    • If T does have a foo member, the compiler will try the first overload but will fail since there is no matching call_foo function so it will again choose the second one and make a std::false_type.

    Working code on ideone: http://ideone.com/erh93I.

    You can put everything in a class if you want:

    template <typename T>
    class HasFoo {
    
        template <typename C>
        static int call_foo (int (C::*)(float));
    
        template <typename C>
        static std::true_type has_foo (decltype(call_foo(&C::foo)));
    
        template <typename C>
        static std::false_type has_foo (...);
    
    public:
        static constexpr bool value = decltype(has_foo<T>(0)){};
    };
    
    0 讨论(0)
  • 2021-01-05 08:38

    Here is a solution with <experimental/type_traits> or <boost/type_traits.hpp>

    #include <experimental/type_traits>
    #include <iostream>
    
    struct A {
      auto foo() { return 0; }
    };
    struct B {
      auto bar() { return 0.0; }
    };
    struct C : public A {
      auto bAr() { return 0.0; }
    };
    struct D : public C {
      auto baR() { return 0.0; }
    };
    
    template <typename T>
    using HasFoo_t = decltype(std::declval<T&>().foo());
    
    int main() {
      std::cout << std::experimental::is_detected_v<HasFoo_t, A> << std::endl;
      std::cout << std::experimental::is_detected_v<HasFoo_t, B> << std::endl;
      std::cout << std::experimental::is_detected_v<HasFoo_t, C> << std::endl;
      std::cout << std::experimental::is_detected_v<HasFoo_t, D> << std::endl;
    }
    
    0 讨论(0)
提交回复
热议问题