c++templatesc++20sfinae

Check for the existence of a template function in c++



#include <type_traits>

template <typename Allocator>
struct AllocatorTraits {
private:
    template <typename U>
    static auto malloc(int) -> decltype(static_cast<void* (U::*)(size_t)>(&U::malloc), std::true_type());

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

    template <typename U, typename T, typename... Args>
    static auto allocate(int) -> decltype(static_cast<T* (U::*)(Args...)>(&U::template allocate<T, Args...>), std::true_type());

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

public:
    static constexpr bool has_malloc = decltype(malloc<Allocator>(0))::value;

    template <typename T, typename... Args>
    static constexpr bool has_allocate = decltype(allocate<Allocator, T, Args...>(0))::value;

};

class TestClass {
public:
    void* malloc(size_t size) noexcept { return nullptr; }

    template <typename T, typename ...Args>
    T* allocate(Args &&...args) noexcept { return nullptr; }
};


static constexpr bool test_malloc = AllocatorTraits<TestClass>::has_malloc;
static constexpr bool test_allocate = AllocatorTraits<TestClass>::has_allocate<int>;

I am using C++20. This works fine if I comment the non template TestClass functions, so commenting out TestClass::malloc, will result in test_malloc being false. This will not compile if I comment out the template function

TestClass::allocate (error C2672: 'AllocatorTraits<TestClass>::allocate': no matching overloaded function found).

Can anybody help me to get this to work as expected please.


Solution

  • Since you can use C++20 just define a concept. They are more handy then trying using SFINAE.

    #include <cstdint>
    #include <type_traits>
    #include <utility>
    
    template <typename T, typename Ret, typename... Args>
    concept has_allocate = requires(T x, Args... args) {
        { x.template allocate<Ret>(std::forward<Args>(args)...) } -> std::same_as<Ret*>;
    };
    
    class HasAllClass {
    public:
        void* malloc(std::size_t) noexcept { return nullptr; }
    
        template <typename T, typename... Args>
        T* allocate(Args&&...) noexcept { return nullptr; }
    };
    
    class Foo { };
    
    static_assert(has_allocate<HasAllClass, int>);
    static_assert(!has_allocate<Foo, int>);
    

    https://godbolt.org/z/KaT6813s1


    Your solution has simple problem. It should go like this:

        template <typename, typename T, typename... Args>
        static std::false_type allocate(...);
    

    Note that you have omitted template parameters.
    https://godbolt.org/z/T7x8jfox8