Search code examples
c++templatesc++17template-meta-programming

Finding if a class template can be instantiated with a set of arguments, arity-wise (in C++17)


I have a template that takes a template template parameter and a pack of type arguments. I want to instantiate the template with the arguments only if the arities match. Something like this:

    // can_apply_t = ???

    template<template<typename...> typename Template, typename... Args>
    struct test {
        using type = std::conditional_t<can_apply_t<Template, Args...>, Template<Args...>, void>;
    };


template<typename> struct unary_template;
template<typename, typename> struct binary_template;

static_assert(std::is_same_v< test<unary_template, int>::type, unary_template<int> >);
static_assert(std::is_same_v< test<binary_template, int>::type, void >);

I fantasized that it would be as simple as this:

template<template<typename... T> typename Template, typename... Args>
struct test {
    using type = std::conditional_t<sizeof...(T) == sizeof...(Args), Template<Args...>, void>;    
};

...but clang++12 says:

error: 'T' does not refer to the name of a parameter pack


Solution

  • fantasized that it would be as simple as this:

    No... not as simple... when you write

    using type = std::conditional_t<can_apply_t<Template, Args...>,
                                    Template<Args...>,
                                    void>;
    

    the Template<Args...> must be an acceptable type also when test of conditional_t is false.

    You need a can_apply_t that directly return Template<Args...> when possible, void otherwise.

    I propose something as the following

    template <template <typename...> class C, typename... Ts>
    auto can_apply_f (int)
      -> std::remove_reference_t<decltype(std::declval<C<Ts...>>())>;
    
    template <template <typename...> class C, typename... Ts>
    auto can_apply_f (long) -> void;
    
    template <template <typename...> class C, typename ... Ts>
    using can_apply_t = decltype(can_apply_f<C, Ts...>(0));
    
    template <template<typename...> typename Template, typename... Args>
    struct test {
      using type = can_apply_t<Template, Args...>;
    };