Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the compiler find the template function X::max(T const&, T const&) through ADL in the code below?

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

like image 475
Wake up Brazil Avatar asked Oct 20 '22 05:10

Wake up Brazil


1 Answers

Standardese

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).

Why your code fails

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).

Better code

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.

like image 156
TemplateRex Avatar answered Oct 31 '22 21:10

TemplateRex