No, this is not related to C ++ grammar, but to lazy C ++ template creation and two-phase search.
In C ++, a dependent name is a name or symbol whose value depends on one or more template parameters:
template <typename T> struct Foo { Foo () { const int x = 42; T::Frob (x); } };
Parsing this fragment separately, not knowing all future values โโof T, the C ++ compiler cannot determine if frob in T function name, a type name, something else, or whether it exists at all.
To give an example of why this matters, imagine some types that you will replace with T:
struct Vietnam { typedef bool Frob;
Put them in our Foo template:
int main () { Foo<Vietnam> fv; // Foo::Foo would declare a type Foo<Football> ff; // Foo::Foo would make a function call Foo<BigVoid> fbv; // Foo::Foo is not defined at all }
Relevant in this is the concept of two-phase search . At the first stage, unsolicited code is analyzed and compiled:
template <typename T> struct Foo { Foo () { const int x = 42;
This first step is what allows compilers to display error messages in the template definition itself.
The second phase will cause errors in your template in combination with the then-known type T.
Some early C ++ compilers will only analyze templates only after they are created; with these compilers, ambiguity is not needed, because at the time of instantiation the template arguments are known. The problem with this single-phase search is that many errors in the template itself will not be detected at all or only at the end of compilation, because the default templates are created lazily, i.e. Only the parts of the class template that are actually used are expanded, plus it gives you more cryptic error messages that may be root in the template argument.
So for two-phase search to work, you must help the compiler. In this case, you should use typename to tell the compiler what you mean by type:
template <typename T> struct Foo { Foo () { const int x = 42; typename T::Frob (x); } };
Now the compiler knows that x is a variable of type Frob :)