I know that typename
and class
keywords are interchangeable in template arguments, but I thought only typename
is allowed for nested classes specification.
Once I've accidentally wrote incorrectly "class
" instead of "typename
" for a nested class.
And I found is that gcc accepts class
there too, so you can write something like:
class std::vector<T>::iterator it;
instead of
typename std::vector<T>::iterator it;
in your template.
Is this a gcc bug or does the standard really allow t开发者_如何转开发his syntax?
UPDATE: example of code:
template <typename T>
void test()
{
class std::vector<T>::iterator it;
}
class a::b
is an elaborated type specifier. Name lookup for an elaborated type specifier ignores non-type names. So if you are parsing a template, you can assume two things:
- When we are instantiating and do name-lookup for
b
, either name lookup gives us a type, or it errors out (wouldn't find any name).
In C++0x for that reason, class a::b
doesn't need typename
(you can't put it anywhere anyway on a elaborated type specifier). C++03 doesn't allow that, so GCC appears to implement the C++0x rules as an extension.
That's not particularly bad. Every real compiler implements rules that are sensible and easy to implement in their C++03 version, on their behalf, even if formally they would need to reject it. However, class a::b
must lookup to a class name. If it's merely a typedef, then the lookup for the elaborated type specifier is invalid.
Note that class a::b
is the only way to ignore non-type names in a lookup (except for arcane cases like before a ::
in a qualified name that have similar special rules). For example
template<typename T>
struct A { typename T::type t; }
struct B { class type { }; int type; };
// invalid, even though GCC accepts that incorrectly
A<B> a;
If you compile to C++0x, and you use class T::type t;
, then the code becomes valid, because class T::type
ignores the data member, but finds the nested class.
Section 14.6 ("Name resolution") in ISO 14886:2003 seems to be the definition of how this is supposed to work. Paragraph 3 says:
A qualified-id that refers to a type and in which the nested-name-specifier depends on a template-parameter (14.6.2) shall be prefixed by the keyword
typename
to indicate that the qualified-id denotes a type, forming an elaborated-type-specifier (7.1.5.3).
No mention of the class
keyword. I think this is a GCC bug.
It fails to compile with Comeau Online (Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09)), so at least one of the two compiler has a bug.
error: typedef "iterator" may not be used in an elaborated
type specifier
class std::vector<T>::iterator it;
Certainly the Standard seems to confirm that typename should be used. From 14.6/2:
A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.
Ideone shows a nice error message when the dependent name doesn't resolve to a class. So your example only works because iterator
is indeed a class
. :)
Edit: MSVC fails to compile even if the dependent is indeed a class,
error C2242: typedef name cannot follow class/struct/union
Edit 2: This seems to be a MSVC bug, as g++ and Comeau online compile just fine if the dependent name is a class.
I'm still loss at what happen. I'd put a comment if putting code there wasn't problematic.
template <typename Foo>
void f()
{
class Foo::bb x;
}
struct X {
typedef int bb;
};
int main()
{
f<X>();
return 0;
}
doesn't compile with gcc with an error during the instantiation. It will compile if X::bb is a class. Como has the same behavior.
Edit, I think I've a better understanding now.
class Foo::Bar;
is an elaborated-class-specifier. Foo::Bar is looked up as a qualified name (3.4.4/3). As it is dependent, the look-up has to be done in the second phase, during instantiation. Then if it isn't found or if it isn't a class-name or enum-name, the elaborated-class-specifier is ill-formed.
TL;DR g++ doesn't seem to have a bug.
精彩评论