2

I struggle with this example in cpp reference.

I thought ordinary + ADL lookups would produce the following set in both cases: f(char) (ordinary lookup), f(int)/f(E) (ADL lookup as it takes into account visibility from the POI). Then overload resolution would select f(E) in the first case and f(int) in the other case.

Could you please explain to me what exactly happens under the hood (lookup, overload resolution) in this case?

Thanks a lot!

Example sample:

void f(char); // first declaration of f
 
template<class T> 
void g(T t) {
    f(1);    // non-dependent name: lookup finds ::f(char) and binds it now
    f(T(1)); // dependent name: lookup postponed
    f(t);    // dependent name: lookup postponed
//  dd++;    // non-dependent name: lookup finds no declaration
}
 
enum E { e };
void f(E);   // second declaration of f
void f(int); // third declaration of f
double dd;
 
void h() {
    g(e);  // instantiates g<E>, at which point
           // the second and the third uses of the name 'f'
           // are looked up and find ::f(char) (by lookup) and ::f(E) (by ADL)
           // then overload resolution chooses ::f(E).
           // This calls f(char), then f(E) twice
    g(32); // instantiates g<int>, at which point
           // the second and the third uses of the name 'f'
           // are looked up and find ::f(char) only
           // then overload resolution chooses ::f(char)
           // This calls f(char) three times
}
Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
nop666
  • 585
  • 2
  • 6
  • @OlafDietsche Nope, that question does not address the template dependent name lookup at all. – Tanveer Badar Aug 13 '20 at 09:40
  • I don't think ADL is involved at all here. You have no namespace or class that `E` belongs to. – super Aug 13 '20 at 10:21
  • @super Thus how does it manage to find the f(E) (see [here](https://godbolt.org/z/xv8orM)) as ordinary lookup occurs in the template definition context. The doc states "**For arguments of enumeration type, the namespace in which the enumeration is defined is added to the set. If the enumeration type is a member of a class, that class is added to the set**". Does it mean in this case that the global namespace is considered by ADL as it is the enum enclosing namespace? In this case, it will consider f(int) as well before overload resolution? – nop666 Aug 13 '20 at 10:47
  • @nop666 My guess is that it's related to the point of instatiation, not ADL. ADL only looks in namespaces that has not been searched by normal lookup. Global namespace is searched by normal lookup. – super Aug 13 '20 at 10:59
  • @nop666 See [this question](https://stackoverflow.com/questions/3810281/name-resolution-and-point-of-instantiation-in-templates) for example. – super Aug 13 '20 at 11:02
  • @super [this question](https://stackoverflow.com/questions/56502624/should-this-function-call-be-ambiguous) and the corresponding standard part seem to show that only ADL can find function declared after template definition. – nop666 Aug 13 '20 at 11:30

1 Answers1

1

ADL looks for function in the namespace (or class) associated to the type of the argument. So if you declare enum E in a namespace X, ADL will only look-up inside namespace X (see bellow).Fundamental types as int do not have any associated namespace. So ADL for fundamental types never find anything.

void f(char); // first declaration of f
 
template<class T> 
void g(T t) {
    f(1);    // non-dependent name: lookup finds ::f(char) and binds it now
    f(T(1)); // dependent name: lookup postponed
    f(t);    // dependent name: lookup postponed
//  dd++;    // non-dependent name: lookup finds no declaration
}

namespace X {
    enum E { e };
    void f(E);   // second declaration of f
    void f(int); // third declaration of f
    }
double dd;

void f(int);//the global namespace is not associated to fundamental types
 
void h() {
    //The associated scope of X::e is namespace X.
    g(X::e);  // instantiates g<E>, at which point
           // the second and the third uses of the name 'f'
           // are looked up and find ::f(char) (by lookup) and X::f(int) and X::f(E) (by ADL)
           // then overload resolution chooses X::f(E).
           // This calls f(char), then X::f(E) twice

    //Fundamental types do not have any associated namespace
    g(32); // instantiates g<int>, at which point
           // the second and the third uses of the name 'f'
           // are looked up and find ::f(char) only
           // then overload resolution chooses ::f(char)
           // This calls f(char) three times
}
Oliv
  • 17,610
  • 1
  • 29
  • 72
  • You are right: if T is fundamental, associated entities and namespaces are empty sets in ADL lookup – nop666 Aug 13 '20 at 10:30
  • @nop666 Indeed fundamental is more precise than built-in as pointer have the associated namespace of the pointee type. – Oliv Aug 13 '20 at 10:39
  • I just realized your example explains the behavior with enclosing namespace as well as with fundamental types. But what about the detection of f(E) in the original example as discussed in the comments? – nop666 Aug 13 '20 at 13:05
  • @nop666 In the original example, the enclosing namespace is the global namespace. This uninspired original example is certainly a source of confusion. – Oliv Aug 13 '20 at 13:23
  • OK, thus you agree that f(E) (and f(int) as well, I think), are found through ADL lookup (that looks up in the global ns for the cppreference exemple) for f(e) even in the uninspired case and, then, f(E) is selected by mean of overload resolutio – nop666 Aug 13 '20 at 13:36
  • @nop666 This is what happens. – Oliv Aug 13 '20 at 15:33