TruLa
TruLa

Reputation: 1147

C++11 ambiguous overload when calling variadic function template

Could you please explain why I'm having error: call of overloaded ‘func(const Test&)’ is ambiguous despite the fact that I use explicit template instantiation?

#include <iostream>

struct Test {
};

void func(const Test &) {
  std::cout << "By Reference" << std::endl;
}

void func(const Test) {
  std::cout << "By Value" << std::endl;
}

template <typename... TArgs>
void wrap(TArgs... args) {
  func(args...);
}

int main() {
  Test t;
  wrap<const Test &>(t);
  return 0;
};

EDIT

The reason of ambiguity is a combination of two factors. The first is that simple overload rules applied in the call func(args...). The second is that simple functions cannot be overloaded by value and const reference. To ensure, one may replace the call wrap<const Test &>(t) with func(static_cast<const Test &>(t)). The error will still be there.

To solve the problem, one may use function template for func and value vs const reference template specialization as showed in the example provided by @lubgr

Thanks everybody to help me to demystify the concept.

Upvotes: 5

Views: 396

Answers (2)

lubgr
lubgr

Reputation: 38267

With the wrap function template explicitly instantiated, imagine the compiler being in the wrap instantiation, knowing that Targs... really is const Test&. Which function overload should then be chosen? It can't prefer one, because the template parameter of wrap doesn't propagate to an ordinary (non-template) function. Instead, simple overload rules apply.

You can see the difference and fix the issue if you change func to be a function template, too:

template <class T> void func(T);

template <> void func<const Test&>(const Test&) {
  std::cout << "By Reference" << std::endl;
}

template <> void func<const Test>(const Test) {
  std::cout << "By Value" << std::endl;
}

The appropriate specialization is called when you explicitly ask for it, then.

template <typename... TArgs>
void wrap(TArgs... args) {
  func<TArgs...>(args...); // Note, the type is specified here, again.
}

Upvotes: 2

paler123
paler123

Reputation: 976

For the same reason the following calls are ambiguous:

#include <iostream>
void foo(int) { std::cout << "val" << std::endl; }
void foo(const int&) { std::cout << "ref" << std::endl; }

int main()
{
  int i = 1;
  foo(i);
}

See here for full discussion of that case.

Upvotes: 3

Related Questions