When I compile this:
#ifndef BTREE_H
#define BTREE_H
#include
template
class btree
{
public:
class node
{
No, this has not to do with C++'s grammar, but with lazy instantiation of C++ templates and two phase lookup.
In C++, a dependent name is a name or symbol whose meaning depends on one or more template parameters:
template
struct Foo {
Foo () {
const int x = 42;
T::Frob (x);
}
};
By parsing that snippet alone, without knowin all future values of T, no C++ compiler can deduce whether frob
in T
is a function name, a type name, something else, or whether it exists at all.
To give an example for why this is relevant, imagine some types you will substitute for T:
struct Vietnam {
typedef bool Frob; // Frob is the name of a type alias
};
struct Football {
void Frob (int) {} // Frob is a function name
};
struct BigVoid {}; // no Frob at all!
Put those into our Foo-template:
int main () {
Foo fv; // Foo::Foo would declare a type
Foo ff; // Foo::Foo would make a function call
Foo fbv; // Foo::Foo is not defined at all
}
Relevant in this is the concept of two-phase lookup. In the first phase, non-dependent code is parsed and compiled:
template
struct Foo {
Foo () {
const int x = 42; // does not depend on T
T::Frob (x); // full check skipped for second phase, only rudimentary checking
}
};
This first phase is what lets compilers emit error messages in the template definition itself.
The second phase would trigger errors of your template in conjunction with the then-known type T.
Some early C++ compilers would only parse templates once you instantiate them; with those compilers, disambiguation wasn't needed, because at the point of instantiation, the template arguments are known. The problem with this one-phase lookup is that many errors in the template itself won't be detected at all or only late in the compile, because templates are by default instantiated lazily, i.e. only parts of a class-template are expanded that are actually used, plus it gives you more cryptic error messages that possibly root in the template-argument.
So in order for two-phase lookup to work, you must help the compiler.
In this case, you must use typename
in order to tell the compiler that you mean a type:
template
struct Foo {
Foo () {
const int x = 42;
typename T::Frob (x);
}
};
The compiler now knows that x is a variable of type Frob :)