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.

Leave a Reply