I want to calculate the sum of all arguments (non-type parameters) passed through a template. I compiled the following program with: g -std=c 17 -g -Wall -o main main.cpp. It seems that I miss something, because I get this errors when compiling:
error: call of overloaded ‘func<N_0>()’ is ambiguous
std::cout << func<N_0>() << std::endl;
error: call of overloaded ‘func<22>()’ is ambiguous
return N func<Next... >();
#include <iostream>
using namespace std;
template <std::size_t T>
std::size_t sum()
{
return T;
}
template <std::size_t N, std::size_t ...Next>
std::size_t sum()
{
return N sum<Next... >();
}
int main()
{
const size_t N_0 = 4;
const size_t N_1 = 11;
const size_t N_2 = 22;
std::cout << sum<N_0>() << std::endl;
std::cout << sum<N_0, N_1, N_2>() << std::endl;
return 0;
}
I found a lot of examples like this:
#include <iostream>
template<typename T>
T adder(T first) {
return first;
}
template<typename T, typename... Args>
T adder(T first, Args... args) {
return first adder(args...);
}
int main() {
const int c = adder(1, 8, 4);
std::cout << c << '\n';
return 0;
}
I am very curious why my code is not working.
CodePudding user response:
In both the first and second code example, calling the function with only one (template) argument results in both function templates being viable. The packs will simply be empty.
However, in overload resolution in the second example the variadic template is considered less specialized than the non-variadic one, basically because the set of possible arguments to call it with is a superset of those that the non-variadic one can be called with. This is only because of the parameter pack in the function parameters. Therefore overload resolution will prefer the non-variadic template as a tie-breaker.
This ordering doesn't apply to your first code example, where the function parameters of both templates are the same.
Therefore overload resolution with a single (template) argument is ambiguous in your first code example, but not ambiguous in the second.
You can specify that the variadic template requires at least two arguments to resolve the ambiguity:
template <std::size_t N, std::size_t M, std::size_t ...Next>
std::size_t sum()
{
// May wrap-around to zero
return N sum<M, Next... >();
}
However, in C 17 and later the whole sum construct can be simplified by use of fold expressions to:
template <std::size_t ...Ns>
std::size_t sum()
{
// May wrap-around to zero
return (Ns ...);
}
(This function should probably be marked noexcept and constexpr in C 17 or consteval in C 20 and one should be careful with many/large arguments since the addition will silently wrap-around if the sum becomes too large for std::size_t to hold.)
