kryomaxim
kryomaxim

Reputation: 117

How can I do a recursion on C++ variadic templates?

Suppose I want to define a C++ function that has no Input Parameters within bracket, but within the <> argument. I have a Parameter pack as Input Arguments. Meaning that I have to write, for example, a function

int calculate<args...>()
{
   return 1 + calculate<some_arg,args...>();
}

And also a base case implementation I have to give. However, I am confronted with a lot of Compiler Errors, because I don't know how to write such a form of recursion out properly. What I have to write before the above function declaration?

template<int... args>

(if data type of args is int; any other data type is also possible in a similar way)? Or what I have to write to avoid Compiler Errors? I also tried

template<int some_arg,int... args>

But I don't know also how to deal with variadic templates (how to unpack them). Any help?

EDIT:

My attempt for one Special case

template<bool... dg>
int calculate<0>()
{
    return 1;
}

Error message for this is:

error: expected initializer before ‘<’ token

Upvotes: 1

Views: 916

Answers (3)

user3457338
user3457338

Reputation: 3

Another way to do recursion allows you to do it in a single function, utilizing the sizeof... operator:

template<int lhs, int... rhs>
int add()
{
    if constexpr(sizeof...(rhs))
    {
        return lhs + add<rhs...>();
    }
    else
    {
        return lhs;
    }
}

This also requires C++17 for if constexpr, but it can do things that fold expressions might not be able to.

Upvotes: 0

Ryan McHale
Ryan McHale

Reputation: 26

If you're using C++17+: refer to dfrib's answer

Here's how you would implement a function to add the elements of a parameter pack using template recursion

template<int arg>
constexpr int add()
{
    return arg;
}

template<int arg1, int arg2, int... args>
constexpr int add()
{
    return arg1 + add<arg2, args...>();
}

Regarding if you wanted to create a special case

template<int arg>
constexpr int calculate()
{
    return arg;
}

template<> int calculate<0>() { return 1; } // special case

template<int arg1, int arg2, int... args>
constexpr int calculate()
{
    return calculate<arg1>() + calculate<arg2,args...>();
}

This would make it to where every time you have a zero in you argument list, it will add 1 instead of 0

Upvotes: 1

dfrib
dfrib

Reputation: 73176

But I don't know also how to deal with variadic templates (how to unpack them). Any help?

As of C++17 you needn't resort to recursion, but can use pack expansion:

#include <iostream>

template<int ...Args>
constexpr int calculate() {
   return (Args + ...);
}

int main() {
    std::cout << calculate<1, 2, 3>();  // 6
}

If you want to allow other types of non-type template parameters, you can make use of a placeholder type (auto) for non-type template parameters, also a C++17 feature:

template<auto ...Args>
constexpr auto calculate() {
   return (Args + ...);
}

As you cannot partially specialize function templates, you will have to use delegation to a class template if you want to provide different implementations for different specializations:

#include <iostream>
#include <ios>

template<auto ...Args>
struct calculate_impl {
    static constexpr auto calc() { return (Args + ...); }
};

template<bool ...Args>
struct calculate_impl<Args...> {
    static constexpr bool calc() { return (Args && ...); }
};

template<auto ...Args>
constexpr auto calculate() {
   return calculate_impl<Args...>::calc();
}

int main() {
    std::cout << calculate<1, 2, 3>();  // 6
    std::cout << std::boolalpha 
        << "\n" << calculate<false,true>()  // false
        << "\n" << calculate<true, true>();  // true
}

Upvotes: 4

Related Questions