Since there are questions about how unqualified names may be dependent, or how unqualified name lookup may apply to dependent names:
Trail parse: Determining how we parse a statement
If in a template a dependent name is encountered, it is always assumed not to name a type, unless the name lookup that is applicable finds that it is a type or we prepend the name with typename
:
template<typename T>
void f() {
T f0; // T is a template type parameter => type
T *f1;
typename T::name g1; // "name" is assumed to be a type.
T::name g0; // "name" cannot be looked up here => non-type
}
This lookup of a name to determine whether it is a type is always done at the point of the template definition for all dependent names: It guides the following parse into a certain direction. In the second statement, we will parse T *f1
as a declaration of a pointer, but not as a multiplication. In the last statement, we assumed during the pre-parse disambiguation that T::name
is not a type and try to parse it as an expression. This will fail, because we will expect a semicolon or some operator after T::name
. This lookup whether or not the name is a type has no influence on the meaning of the name in later phases: It won't yet bind the name to any declaration.
Actual parse
After we determined what names are types and what not, we will actually parse the template. Names that are not dependent - that is, those that are not looked up in a scope that is dependent or that are not explicitly made dependent by other rules - are looked up at the point where they are used in the template, and their meaning is not influenced by any declaration visible when instantiating.
Names that are dependent are looked up when instantiating, both in the template definition where they are used, and where their template is instantiated. This is true also for unqualified names that are dependent:
template<typename T>
struct Bar {
void bar() { foo(T()); }
};
namespace A {
struct Baz { };
void foo(Baz); // found!
}
int main() { Bar<A::Baz> b; b.bar(); }
The unqualified foo
is made dependent by the Standard because the argument T()
is type-dependent. When instantiating, we will look for functions called foo
using unqualified name lookup around the template definition, and using argument dependent lookup (meaning, roughly, in the namespace of T
) around both the template definition and the point where we instantiate it (after main
). Argument dependent lookup will then find foo
.
If Bar
now has a dependent base class, unqualified lookup must ignore that dependent base class:
template<typename T>
struct HasFoo { };
template<typename T>
struct Bar : HasFoo<T> {
void bar() { foo(T()); }
};
namespace A {
struct Baz { };
void foo(Baz); // found!
}
template<>
struct HasFoo<A::Baz> {
void foo();
};
int main() { Bar<A::Baz> b; b.bar(); }
This must still find A::foo
, despite the fact that unqualified name-lookup would find a class member function if it were done (ADL will not find additional functions if a class member function was found). But unqualified namelookup will not find that function, because it's a member of a dependent base class, and those are ignored during unqualified name lookup. Another interesting case:
template<typename>
struct A {
typedef int foo;
operator int() {
return 0;
}
};
// makes sure applicable name-lookup
// classifies "foo" as a type (so parsing will work).
struct TypeNameSugar {
typedef int foo;
};
template<typename T>
struct C : A<T>, TypeNameSugar {
void c() {
A<T> *p = this;
int i = p->operator foo();
}
};
int main() {
C<void>().c();
}
The Standard states that during lookup of foo
in the operator foo
name, we will lookup independently in both the scope of p->
(which is in the scope of class A<T>
), and the scope in which the complete expression appears (which is the scope of C<T>::c
as an unqualified name), and compare the two names, if found, whether they designate the same type. If we would not ignore the dependent base class A<T>
during the lookup in the scope of the complete expression, we will find foo
in two base classes, thus having an ambiguity. Ignoring A<T>
will mean we find the name once when we do lookup into p->
, and another time when we do lookup into TypeNameSugar
.