Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two-phase function template compilation: not *only* ADL is employed in the 2nd phase?

I'm wondering why the following code compiles.

#include <iostream>

template<class T> 
void print(T t) {
    std::cout << t;
}

namespace ns {
    struct A {};
}

std::ostream& operator<<(std::ostream& out, ns::A) {
    return out << "hi!";
}

int main() {
    print(ns::A{}); 
}

I was under impression that at the instantiation point unqualified dependent names are looked-up via ADL only - which should not consider the global namespace. Am I wrong?

like image 381
Igor R. Avatar asked Nov 10 '19 21:11

Igor R.


1 Answers

This is an interesting case. The workings of name lookup as you describe them is summarized here:

[temp.dep.candidate] (emphasis mine)

1 For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:

  • For the part of the lookup using unqualified name lookup, only function declarations from the template definition context are found.

  • For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either the template definition context or the template instantiation context are found.

If the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.

The bit I highlighted is the crux of the matter. The description for "ADL only" is for function calls of the from foo(bar)! It does not mention calls that result from an overloaded operator. We know that calling overloaded operators is equivalent to calling a function, but the paragraph speaks of expressions in a specific form, that of a function call only.

If one was to change your function template into

template<class T> 
void print(T t) {
    return operator<< (std::cout, t);
}

where now a function is called via postfix-expression notation, then wo and behold: GCC emits an equivalent error to Clang. It implements the above paragraph reliably, just not when it comes to overloaded operator calls.

So is it a bug? I would say it is. The intent is surely that overloaded operators be found like named functions (even when called from their respective expression form). So GCC needs to be fixed. But the standard could use a minor clarification of the wording too.

like image 70
StoryTeller - Unslander Monica Avatar answered Oct 18 '22 07:10

StoryTeller - Unslander Monica