GCC和class关键字

gcc and class keyword

本文关键字:关键字 class GCC      更新时间:2023-10-16

我知道typenameclass关键字在模板参数中是可互换的,但我认为只有typename是允许嵌套类规范的。

有一次我不小心写错了"class"而不是"typename"的嵌套类。我发现gcc也接受class,所以你可以这样写:

class std::vector<T>::iterator it;
instead of
typename std::vector<T>::iterator it;

这是一个gcc错误还是标准真的允许这种语法?

更新:代码示例:

template <typename T>
void test()
{
     class std::vector<T>::iterator it;
}

class a::b是一个详细的类型说明符。详细类型说明符的名称查找忽略非类型名称。因此,如果您正在解析模板,可以假设两件事:

  • 当我们对b进行实例化和名称查找时,名称查找要么给我们一个类型,要么出错(找不到任何名称)。
由于这个原因,在c++ 0x中,class a::b不需要typename(无论如何,你不能把它放在一个详细的类型说明符的任何地方)。c++ 03不允许这样做,因此GCC似乎将c++ 0x规则作为扩展来实现。

那不是特别糟糕。每一个真正的编译器都实现了一些规则,这些规则在它们的c++ 03版本中很容易实现,即使在形式上它们需要拒绝它。但是,class a::b 必须查找类名。如果它只是一个类型定义,那么查找详细的类型说明符是无效的。

请注意,class a::b是在查找中忽略非类型名称的唯一方法(除了像具有类似特殊规则的限定名中的::之前的晦涩情况)。例如

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;

如果您编译到c++ 0x,并且使用class T::type t;,那么代码将有效,因为class T::type忽略数据成员,但会找到嵌套类。

ISO 14886:2003中的第14.6节("名称解析")似乎是如何工作的定义。第三段说:

一个限定id引用一个类型,并且嵌套名称说明符依赖于模板参数(14.6.2)的限定id应该用关键字typename作为前缀,以表明限定id表示一个类型,形成详细类型说明符(7.1.5.3)。

没有提到class关键字。

使用Comeau Online (Comeau C/c++ 4.3.10.1 (Oct 6 2008 11:28:09))无法编译,因此两个编译器中至少有一个存在错误。

error: typedef "iterator" may not be used in an elaborated
          type specifier
       class std::vector<T>::iterator it;

标准似乎肯定应该使用typename。从14.6/2:

在模板声明或定义中使用且依赖于模板形参的名称被假定为不命名类型,除非适用的名称查找找到类型名称或该名称由关键字typename限定。

Ideone在依赖名称不能解析为类时显示了一个很好的错误消息。所以你的例子只工作,因为iterator确实是一个class。:)


编辑:即使依赖项确实是一个类,MSVC也无法编译,

错误C2242: typedef名称不能跟随类/结构/联合

编辑2 :这似乎是一个MSVC错误,因为如果依赖的名称是一个类,g++和Comeau在线编译就可以了。

我还是不明白发生了什么。如果放代码没有问题,我就会加注释。

template <typename Foo>
void f()
{
    class Foo::bb x;
}
struct X {
    typedef int bb;
};
int main()
{
    f<X>();
    return 0;
}

不能用GCC编译,在实例化期间出现错误。如果X::bb是一个类,它将编译。Como具有相同的行为。

编辑,我想我现在理解得更好了。

class Foo::Bar;

是一个详细的类说明符。Foo::Bar作为限定名查找(3.4.4/3)。由于它是依赖的,查找必须在实例化期间的第二阶段完成。然后,如果没有找到它,或者它不是类名或enum-name,则详细的类说明符是错误的。

TL;DR g++似乎没有bug