2
votes

I have written a variadic template function with recursive evaluation. For the final parameter I implemented a specialization without the variadic pack and everything worked fine.

Now I want to convert the variadic function parameters into template parameters.

This is my try:

template<typename N, int p> // specialization
N constexpr myadd(const N &n)
{
    return n + p;
}

template<typename N, int p, int ... v> // variadic
N constexpr myadd(const N &n)
{
    return myadd<N, v...>(n) + p;
}

int testfun()
{
    return myadd<int, 2>(7);
}

gcc and clang are reporting an ambiguous overload and can not decide between 'specialization' and 'variadic' with an empty parameter pack.

I tried removing the specialization and check for the pack size in the variadic template, but without the specialization the compilers are failing to deduce parameter 'p'.

2
Notice that there are no specializations, just different overloads. - Jarod42

2 Answers

1
votes

As noted in this answer, the variadic pack can be indeed empty. But you can specialize the case of a non-empty variadic pack with SFINAE.

For example:

#include <iostream>
#include <type_traits>

template<typename N, int p> // specialization
N constexpr myadd(const N &n)
{
    std::cout << "specialization" << std::endl;
    return n + p;
}

template<typename N, int p, int ... v, class = std::enable_if_t<(sizeof...(v) > 0)>> // variadic
N constexpr myadd(const N &n)
{
    std::cout << "variadic" << std::endl;
    return myadd<N, v...>(n) + p;
}

int main()
{
    std::cout <<  myadd<int, 2>(7) << std::endl;
    std::cout <<  myadd<int, 2, 4>(7) << std::endl;
    
    return 0;
}

See it Live on Coliru.

1
votes

The parameter pack can indeed be empty. You can avoid the ambiguity by changing the specialization to handle the case of not adding anything:

template<typename N> // specialization
N constexpr myadd(const N &n)
{
    return n;
}

template<typename N, N p, N ... v> // variadic
N constexpr myadd(const N &n)
{
    return myadd<N, v...>(n) + p;
}

With C++17, you can simplify this by using fold expressions:

template<typename N, N ... v> // variadic
N constexpr myadd(const N &n)
{
    return (v + ... + n);
}