Search code examples
c++template-meta-programming

Can you detect template member of a class using std::is_detected


I use std::experimental::is_detected to determine if class has certain member functions:

#include <utility>
#include <experimental/type_traits>

template<typename USC>
class Descriptor
{
private:

    template<class T>
    using has_member1_t = 
        decltype(std::declval<T>().member1(std::declval<std::vector<char> &>()));

public:

    static constexpr bool has_member1 = 
        std::experimental::is_detected_convertible_v<long long,
                                                     has_member1_t,
                                                     USC>;
};

The problem is I also need to determine if class has certain template member function with following signature:

template<typename Derived>
int member2(Eigen::ArrayBase<Derived> &&)

I've tried to do it like so:

//Inside Descriptor

template<class T, class U>
using has_member2_t = 
    decltype(std::declval<T>().member2(std::declval<Eigen::ArrayBase<U> &&>()));

static constexpr bool has_member2 = 
    std::experimental::is_detected_convertible_v<long long,
                                                 has_member2_t,
                                                 USC>;

but it doesn't work. As I'm not really experienced with C++ TMP I would like to know is there a way to achieve this with std::experimental::is_detected or some other utility?


Solution

  • If c++20 is an option, this kind of code has been made a lot easier to both read and write by using concepts.

    #include <iostream>
    #include <vector>
    
    struct Foo {
        int member1(int) {
            return 1;
        }
    
        template <typename T>
        double member2(std::vector<T>) {
            return 2.5;
        }
    };
    
    struct Bar {};
    
    template <typename T>
    concept MyConcept = requires (T t) {
        { t.member1(0) } -> std::same_as<int>;              // We can check return type
        { t.template member2<int>(std::vector<int>{}) };    // but we don't have to
    };
    
    int main() {
        static_assert(MyConcept<Foo>);
        static_assert(!MyConcept<Bar>);
    }
    

    The issue with your attempt is that you are not passing anything as U for the check. I would also modify it to use the .template member2<...> syntax to explicitly check for a template.

    Here is an example of that.

    #include <iostream>
    #include <vector>
    #include <utility>
    #include <experimental/type_traits>
    
    struct Foo {
        int member1(int) {
            return 1;
        }
    
        template <typename T>
        double member2(std::vector<T>) {
            return 2.5;
        }
    };
    
    struct Bar {};
    
    template<class T, class U>
    using has_member2_t = 
        decltype(std::declval<T>().template member2<U>(std::declval<std::vector<U> &&>()));
    
    int main() {
        static constexpr bool has_member2_Foo = 
        std::experimental::is_detected_convertible_v<long long,
                                                     has_member2_t,
                                                     Foo, double>;
    
        static constexpr bool has_member2_Bar = 
        std::experimental::is_detected_convertible_v<long long,
                                                     has_member2_t,
                                                     Bar, double>;
    
        static_assert(has_member2_Foo);
        static_assert(!has_member2_Bar);
    }