3

The following program compiles (live demo), but I don't understand, why.

namespace N {
    struct S {};
}


void Foo(N::S);


namespace Lib {
    template <class T>
    void Call() { Foo(T{}); }

    void Foo();
}


int main()
{
    Lib::Call<N::S>();
}

Shouldn't Lib::Foo hide ::Foo? Foo in Call is a dependent name, and evaluation of dependent names are supposed to be postponed until the instantiation of the template. How does name lookup work in this case?

In namespace Lib Foo(N::S{}) can be called before the declaration of void Foo();, but it cannot be called after the declaration, because Lib::Foo hides ::Foo. Lib::Call<N::S>(); is after the declaration, so when binding the name Foo here, hiding should be in effect, shouldn't it?

Dr. Gut
  • 2,053
  • 7
  • 26
  • 1
    I'm not quite sure, but after some research: If `Foo` is nondependent, ordinary lookup and ADL is executed in the template definition context. If `Foo` is dependent, ordinary lookup is executed in the template definition context, but ADL is executed at the point of instantiation. Our case is the second, thus `::Foo` is found by ordinary lookup. The `Lib::Foo` declaration does not hide `::Foo`, because it is after the context in which ordinary lookup searches. Someone please confirm my supposition, maybe in an answer. – Dr. Gut Aug 16 '20 at 23:06

1 Answers1

2

All non-ADL lookup for a name used in a template is from the template definition, even though the results are not used until instantiation in cases where ADL might contribute declarations found from the instantiation context.

You can think of this in a “spatial” sense—the two parts of the dependent lookup happen from different places (limiting what they find) at the same time—or in a “temporal” sense—the non-dependent-ADL lookup happens while parsing the template, and the results are saved and later merged with the dependent ADL results during instantiation. The former is how the standard describes it (to avoid describing compilation as a time-dependent process), but the latter intuitively accounts for the treatment of dependent names for parsing purposes (and thus when typename and template are required).

The standard rules for this are scattered across several cases for different kinds of names ([temp.res]/1.3, [temp.nondep]/1, and [temp.dep.candidate]/1 in C++20).

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • Thanks. Could you answer the actual question? E.g.: `Lib::Foo` does not do hiding, because a declaration of the name `Foo` is found (`::Foo`) from the definition of function template `Call`. – Dr. Gut Aug 18 '20 at 17:15
  • @Dr.Gut: `Lib::Foo` can’t hide anything in a lookup from a point where it hasn’t even been declared, which I explained is what happens here. – Davis Herring Aug 18 '20 at 17:34
  • I suppose the first sentence "All non-ADL lookup for a name defined..." has a typo. The word "defined" should be "used", shouldn't it? – Dr. Gut Sep 06 '20 at 22:48
  • @Dr.Gut: Yes; the word “defined” must have crept in from “definition context”. Editing. – Davis Herring Sep 07 '20 at 01:26