简体   繁体   中英

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);
}

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

In c++17, it would simply be

template <typename... Args>
int32_t Sum()
{
    return (CodeByType<Args>::Value + ...); // Fold expression
}

In C++11, you may do:

template <typename... Args>
int32_t Sum()
{
    int32_t res = 0;
    const int32_t dummy[] = {0, (res += CodeByType<Args>::Value)...};
    static_cast<void>(dummy); silent warning about unused variable
    return res;
}

My memories of the template mechanism are old but if I recall correctly, their information is erased at a certain point in the compilation process.

My guess is that in the second case, the functions get distinguished not by the difference in the template types, but by the difference in the arguments.

In your case, you have no arguments, so stripped of the template information the two overloaded versions are equal and it cannot distinguish between them when you call it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM