Abhishek Kumar
Abhishek Kumar

Reputation: 769

Why is the following template function overload resolution ambiguous?

Based on my understanding of the C++'s partial ordering algorithm, it seems that the first one is strictly a subset of the second definition. Therefore, whenever both can be chosen, the first one should be preferred. But I am getting the following error message:

p_o.cpp:13:10: error: call to 'f' is ambiguous
  return f<R>(args...);
         ^~~~
p_o.cpp:17:12: note: in instantiation of function template specialization 'f<int, double, int>' requested here
  auto a = f<int>(0.3, 1);
           ^
p_o.cpp:7:3: note: candidate function [with T = int, Ts = <>]
T f(T a, Ts ...args) {
  ^
p_o.cpp:12:3: note: candidate function [with R = int, T = int, Ts = <>]
R f(T a, Ts ...args) {
  ^
1 error generated.

Could someone please explain where am I going wrong? I am new to meta-programming.

#include <tuple>

using namespace std;

template <typename T, typename ...Ts>
T f(T a, Ts ...args) {
  return a;
}

template <typename R, typename T, typename ...Ts>
R f(T a, Ts ...args) {
  return f<R>(args...);
}

int main() {
  auto a = f<int>(0.3, 1);
  static_assert(is_same<int, decltype(a)>::value);
}

Upvotes: 3

Views: 69

Answers (1)

Rerito
Rerito

Reputation: 6086

You can boil down your offending case to the following:

f<int>(1);

Which can be interpreted as a call to:

R f<R, T, Ts...>(T, Ts...) // with `R = int`, `T=int` and `Ts = <>`

or a call to:

T f<T, Ts>(T, Ts...) // with `T = int` and `Ts = <>`

Note that the more specialized reasoning of partial template ordering applies on argument types and here, they both are the same therefore, the 2 overloads are considered equally valid for your call which leads to the ambiguity.

To resolve that you need something to disqualify one of the two overloads. It seems you want to extract the first member of your pack matching the requested type... To do so you could use a SFINAE based design:

template< typename R, typename T, typename... Ts >
std::enable_if_t< std::is_same< R, T >::value, R > f( T a, Ts... )
{
    return a;
}

template< typename R, typename T, typename... Ts >
std::enable_if_t< ! std::is_same< R, T >::value, R > f( T, Ts... args )
{
    return f< R >( args... );
}

Upvotes: 2

Related Questions