How can I check whether a member function has const overload?

前端 未结 5 704
北海茫月
北海茫月 2021-01-19 05:30

Lets say I have

struct foo {
    void ham() {}
    void ham() const {}
};

struct bar {
    void ham() {}
};

Assuming I have a templated fu

相关标签:
5条回答
  • 2021-01-19 05:38

    Detector (like is_detected):

    template <typename...>
    using void_t = void;
    
    template <typename T, template <typename> class D, typename = void>
    struct detect : std::false_type {};
    
    template <typename T, template <typename> class D>
    struct detect<T, D, void_t<D<T>>> : std::true_type {};
    

    Sample member verifier:

    template <typename T>
    using const_ham = decltype(std::declval<const T&>().ham());
    

    Test:

    static_assert(detect<foo, const_ham>::value, "!");
    static_assert(!detect<bar, const_ham>::value, "!");
    

    DEMO

    0 讨论(0)
  • 2021-01-19 05:39

    Another option is to simulate void_t(to appear officially in C++17), which makes use of expression SFINAE to make sure your function is call-able on a const instance, regardless of its return type.

    #include <iostream>
    #include <type_traits>
    
    struct Foo
    {
        void ham() const;
        void ham();
    };
    
    struct Bar {
        void ham() {}
    };
    
    template<typename...>
    using void_t = void;
    
    template<typename C, typename = void>
    struct has_const_ham: std::false_type{};
    
    template<typename C> // specialization, instantiated when there is ham() const
    struct has_const_ham<C, void_t<decltype(std::declval<const C&>().ham())>> : 
        std::true_type{};
    
    int main()
    {
        std::cout << std::boolalpha;
        std::cout << has_const_ham<Foo>::value << std::endl;
        std::cout << has_const_ham<Bar>::value << std::endl;
    }
    

    EDIT

    If you want to enforce the return type, then derive the specialization from std::is_same, like

    template<typename C> // specialization, instantiated when there is ham() const
    struct has_const_ham<C, void_t<decltype(std::declval<const C&>().ham())>> : 
        std::is_same<decltype(std::declval<const C&>().ham()), void> // return must be void
    {}; 
    

    Live on Coliru

    0 讨论(0)
  • 2021-01-19 05:48

    Here is a solution without macros which also does not care about return types:

    template <typename T>
    struct is_well_formed : std::true_type
    {
    };
    
    template <typename T, typename = void>
    struct has_const_ham : std::false_type
    {
    };
    
    template <typename T>
    struct has_const_ham<T,
                         typename std::enable_if<is_well_formed<decltype(
                             std::declval<const T&>().ham())>::value>::type>
        : std::true_type
    {
    };
    
    
    static_assert(has_const_ham<foo>::value, "oops foo");
    static_assert(!has_const_ham<bar>::value, "oops bar");
    
    0 讨论(0)
  • 2021-01-19 05:56

    SFINAE over and over again. Here is another option which is unspecified about the return types but lets you specify the arguments.

    (For comparison: the approach by @Jarod42 checks the exact signature, return type + arguments, the other void_t expression sfinae stuff up to now checks only whether ham() can be called.)

    Plus, it works with the current MSVC 2015 Update 1 version (unlike the usual void_t stuff).

    template<typename V, typename ... Args>
    struct is_callable_impl
    {
        template<typename C> static constexpr auto test(int) -> decltype(std::declval<C>().ham(std::declval<Args>() ...), bool{}) { return true; }
        template<typename> static constexpr auto test(...) { return false; }
        static constexpr bool value = test<V>(int{});
        using type = std::integral_constant<bool, value>;
    };
    
    template<typename ... Args>
    using is_callable = typename is_callable_impl<Args...>::type;
    

    Use it as

    struct foo
    {
        void ham() {}
        void ham() const {}
        int ham(int) const {}
    };
    
    int main()
    {
         std::cout
          <<is_callable<foo>::value                //true
          <<is_callable<const foo>::value          //true
          <<is_callable<const foo, int>::value     //true
          <<is_callable<const foo, double>::value  //also true, double is converted to int
          <<is_callable<const foo, std::string>::value  //false, can't call foo::ham(std::string) const
          <<std::endl;
    }
    

    Demo on Coliru

    For the "newest" sfinae stuff, however, I suggest you have a look at boost.hana.

    0 讨论(0)
  • 2021-01-19 05:57

    With

    #define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)               \
        template <typename U>                                                   \
        class traitsName                                                        \
        {                                                                       \
        private:                                                                \
            template<typename T, T> struct helper;                              \
            template<typename T>                                                \
            static std::uint8_t check(helper<signature, &funcName>*);           \
            template<typename T> static std::uint16_t check(...);               \
        public:                                                                 \
            static                                                              \
            constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
        }
    
    DEFINE_HAS_SIGNATURE(has_ham_const, T::ham, void (T::*)() const);
    

    And then

    static_assert(has_ham_const<foo>::value, "unexpected");
    static_assert(!has_ham_const<bar>::value, "unexpected");
    

    Demo

    0 讨论(0)
提交回复
热议问题