When a compiler parses a C++ file, it usually builds a symbol table of identifier to types. This is important when invoking templates, as the C++ compiler will essentially do a "substitution check" to make sure that "things all work out."
The problem is this ambiguous statement:
T::bar * p;
This can be interpreted as either:
- static member variable on type
T
named bar
multiplied by p
: ? operator*(? T::bar, ? p)
- declaration of a pointer of type
T::bar*
with name p
.
This creates some sort of ambiguity when parsing, and this results in a compiler error:
g++ main.cpp && ./a.out
main.cpp: In function 'void foo(const T&)':
main.cpp:9:13: error: 'p' was not declared in this scope
9 | T::bar * p;
| ^
main.cpp: In instantiation of 'void foo(const T&) [with T = StructWithBarAsType]':
main.cpp:14:9: required from here
main.cpp:9:11: error: dependent-name 'T::bar' is parsed as a non-type, but instantiation yields a type
9 | T::bar * p;
main.cpp:9:11: note: say 'typename T::bar' if a type is meant
main.cpp:9:11: error: dependent-name 'T::bar' is parsed as a non-type, but instantiation yields a type
In order to clear up this parsing error, C++ has a "syntax modification" to "signal" that an identifier in a template is intended to be a type name and not a variable name. This "syntax modification comes in the form of typename
:
struct StructWithBarAsType {
typedef int bar;
};
template <typename T>
void foo(const T& t)
{
// declares a pointer to an object of type T::bar
typename T::bar * p;
}
int main() {
StructWithBarAsType x;
foo(x);
}
and this compiles.
Note that similar problems happen related to parsing in C++. See the most vexing parse.