Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous call to variadic template function with no parameters?

When running this:

template <typename T>
struct CodeByType
{
    static const int32_t Value = 7;
};

template <>
struct CodeByType<int>
{
    static const int32_t Value = 1;
};

template <typename Arg, typename... Args>
int32_t Sum()
{
    // The compiler complains on this line
    return Sum<Arg>() + Sum<Args...>();
}

template <typename Arg>
int32_t Sum()
{
    return CodeByType<Arg>::Value;
}

int main()
{
    auto sum = Sum<int, char, double>();
}

I'm getting:

Error C2668 'Sum': ambiguous call to overloaded function

Can someone please explain why and how to overcome it?

This looks awfully similar to the below code, which does compile, so I suppose it has something to do with Sum not accepting any actual parameters.

template <typename T>
T adder(T first) {
    return first;
}

template<typename T, typename... Args>
T adder(T first, Args... rest) {
    return first + adder(rest...);
}

int main()
{
    auto sum = adder(1, 7);
}
like image 759
Shmoopy Avatar asked Jan 03 '23 08:01

Shmoopy


1 Answers

If you reduce your code to just:

Sum<int>();

You get a more helpful error message:

31 : <source>:31:16: error: call to 'Sum' is ambiguous
    auto sum = Sum<int>();
               ^~~~~~~~
17 : <source>:17:9: note: candidate function [with Arg = int, Args = <>]
int32_t Sum()
        ^
24 : <source>:24:9: note: candidate function [with Arg = int]
int32_t Sum()
        ^
1 error generated.

So it is clearer that there is an overload ambiguity between the first overload with Args = <> and the second one. Both are viable.

One would might think as specialization for a solution:

template <typename Arg>
int32_t Sum<Arg>()
{
    return CodeByType<Arg>::Value;
}

which would indeed solve the issue, had it been allowed by the standard. Partial function specializations are not allowed.

C++17 solution:

This is the most elegant solution:

constexpr if to the rescue:

template <typename Arg, typename... Args>
int32_t Sum()
{
    if constexpr(sizeof...(Args) == 0)
      return CodeByType<Arg>::Value;
    else
      return Sum<Arg>() + Sum<Args...>();
}

C++14 solution

We use SFINAE to enable/disable the function we want. Please note the function definition order had to be reversed.

template <typename Arg, typename... Args>
auto Sum() -> std::enable_if_t<(sizeof...(Args) == 0), int32_t>
{
      return CodeByType<Arg>::Value;
}


template <typename Arg, typename... Args>
auto Sum() -> std::enable_if_t<(sizeof...(Args) > 0), int32_t>
{
      return Sum<Arg>() + Sum<Args...>();

}

C++11 solution

just replace std::enable_if_t<> with typename std::enable_if<>::type

like image 122
bolov Avatar answered Feb 06 '23 19:02

bolov