Reputation: 961
Here is a bit of code:
template<class T>
inline void bar(T& t) {
foo(t); // intention is that foo() is found by ADL
}
struct Wig {
int i;
};
void foo(int){ }
// some convenience overload
inline void bar(const Wig& w) { foo(w.i); }
// The bit that if uncommented, "fixes" the problem
//inline void bar(Wig& w) { foo(w.i); }
int main()
{
Wig w;
bar(w);
return 0;
}
Clang 3.5 and Gcc 4.7, spit out the following error:
template-function-overload.cpp:12:4: error: no matching function for call to 'foo'
foo(t);
^~~
template-function-overload.cpp:29:4: note: in instantiation of function template specialization
'bar<Wig>' requested here
bar(w);
^
template-function-overload.cpp:19:6: note: candidate function not viable: no known conversion from
'Wig' to 'int' for 1st argument
void foo(int){ }
^
So it doesn't look like a compiler issue.
Also, commenting out the non-const overload fixes the error.
Why is this code incorrect, and why a non-const overload required?
Upvotes: 0
Views: 1169
Reputation: 137394
For the bar(w)
call, overload resolution compares
void bar<Wig>(Wig&); // instantiated from the template
void bar(const Wig &);
Binding to a reference to a less cv-qualified type is better ([over.ics.rank], bullet 3.2.6), so the first signature is chosen. That template attempts to call foo
on a Wig
, but there's no such thing, hence the error.
With the extra non-const overload, we are now comparing (plus the const Wig &
that's worse than either):
void bar<Wig>(Wig&); // instantiated from the template
void bar(Wig&);
The parameter types are identical, so the two are indistinguishable by ranking conversion sequences, and instead the tiebreaker in [over.match.best] bullet 1.6 selects the non-template over the function template specialization.
Upvotes: 7