Search code examples
c++variadic-templates

How best to write the base case of this recursive template?


I'm trying to write a template function that has this behavior:

// count_template_params<>()               == 0
// count_template_params<int>()            == 1
// count_template_params<int, int>()       == 2
// count_template_params<int, int, int>()  == 3
// etc...

This is the closest I've been able to achieve:

#include <iostream>

template<int DUMMY>
constexpr int count_template_params() {
    return 0;
}

template<int DUMMY, class T, class... SS>
constexpr int count_template_params() {
    return 1 + count_template_params<DUMMY, SS...>();
}

int main(int argc, char **argv) {
    int count = count_template_params<0, int, int, int, int>();
    std::cout << "Count=" << count << std::endl;
}

That works, it prints '4' as expected. However, it has the ugly DUMMY parameter. If I remove the dummy parameter, the compiler mistakes the recursion base case for template specialization. Is there a way to do this that doesn't have the DUMMY parameter?


Solution

  • As the other answer mentions, this particular example can be solved with sizeof...() In fact, it cannot be solved with recursion because you are using a function template which does not allow for partial specialization.

    There are, however, still many times when recursion is the best solution for working with meta-programming and templates (e.g. see this answer). The canonical approach for your particular example would be something like the following using struct which allows for the necessary partial specialization.

    #include <type_traits>
    
    template<class... Ts>
    struct count;
    
    template<>
    struct count<> {
        static constexpr auto value = 0;
    };
    
    template<class T, class... Ts>
    struct count<T, Ts...> {
        static constexpr auto value = 1 + count<Ts...>::value;
    };
    
    template<class... Ts>
    inline constexpr auto count_v = count<Ts...>::value;
    
    static_assert(count_v<> == 0);
    static_assert(count_v<int> == 1);
    static_assert(count_v<int,int> == 2);
    static_assert(count_v<int,int,int> == 3);
    
    int main(int argc, const char *argv[]) {
        return 0;
    }
    

    A typical maneuver is to accept template arguments as class T, class... Ts in order to do processing on the T and then recurse on the Ts... as was done above.