A quote from the Standard is appreciated.
#include <iostream>
namespace X {
class A {};
}
template <typename T>
inline T const& max(T const& a, T const& b, T const& c)
{
return max(max(a, b), c);
}
inline X::A const& max(X::A const& a, X::A const& b)
{
std::cout << "non-template" << '\n';
return a;
}
int main()
{
X::A a, b, c;
max(a, b, c);
}
namespace X {
template <typename T>
inline T const& max(T const& a, T const& b)
{
std::cout << "template" << '\n';
return a;
}
}
Live example
The call to max()
in the example entails a dependent name because its arguments depend on the template parameter T
. Two-phase name lookup for such dependent names is defined in the Standard as follows:
14.6.4.2 Candidate functions [temp.dep.candidate]
1 For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:
— For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition context are found.
— For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.
Unqualified lookup is defined by
3.4.1 Unqualified name lookup [basic.lookup.unqual]
1 In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
and argument-dependent lookup (ADL) as
3.4.2 Argument-dependent name lookup [basic.lookup.argdep]
1 When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template argument).
In your example, unqualified lookup and ADL both do not find any overload at the point of definition because the compiler has not seen any two-argument max()
yet. ADL also applies at the point of instantiation, and at that time, the compiler has seen the two-argument template max(T, T)
which is the only one that can be called. (the difference is that template instantion happens after the entire translation unit has been parsed).
You should fix your code by putting the non-template max(X::A, X::A)
overload inside the namespace X
and move the template max(T, T)
out of it.
#include <iostream>
// generic code
template <typename T>
inline T const& max(T const& a, T const& b)
{
std::cout << "template" << '\n';
return a;
}
template <typename T>
inline T const& max(T const& a, T const& b, T const& c)
{
using ::max; // fallback if no user-defined max
return max(max(a, b), c);
}
// X specific code
namespace X {
class A {};
inline X::A const& max(X::A const& a, X::A const& b)
{
std::cout << "non-template" << '\n';
return a;
}
} // namespace X
int main()
{
X::A a, b, c;
max(a, b, c);
}
Live-Example that prints "non-template" twice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With