多个嵌套的依赖名称 - 在哪里粘贴typename关键字?

时间:2011-07-10 17:42:13

标签: c++ templates typename dependent-name

这个问题的灵感来自this other question。在尝试回答这个问题时,我明白自己有很多问题。所以......考虑以下几点:

struct S1
{
    enum { value = 42 };
};

template <class T> struct S2
{
    typedef S1 Type;
};

template <class T> struct S3
{
    typedef S2<T> Type; 
};

template <class T> struct S4
{
    typedef typename T::Type::Type Type;  //(1)//legal?
    enum {value = T::Type::Type::value }; //(2)//legal?
};

int main()
{
    S4<S3<S2<S2<S1> > > >::value;
}

这与MSVC9.0和Online Comeau成功编译。但是, 困扰我的是我不理解{1}在(1)中引用的内容以及为什么我们在(2)中不需要typename

我已经尝试过这两种语法(合成语法?),我认为它们应该在MSVC上都失败了:

typename

    typedef typename T::typename Type::Type Type;
    enum {value = typename T::typename Type::Type::value }; 

当然,解决方法是使用这样的连续 typedef typename (typename T::Type)::Type Type; enum {value = (typename (typename T::Type)::Type)::value };

typedef

除了好的风格,我们在语法上 必须 使用我提到的解决方法吗?

其余只是一个有趣的例子。无需阅读。与问题无关。

请注意,尽管MSVC接受原始的奇怪语法而没有多个 typedef typename T::Type T1; typedef typename T1::Type Type; enum { value = Type::value}; s(我的意思是(1)和(2)),但它会导致奇怪的行为,如上述问题所示。我想我也会在这里以简洁的形式提出这个例子:

typename

这不编译。我提到的解决方法解决了这个问题,但我确信这里的问题是我最初的问题 - 缺少typename,但你真的不知道在哪里贴一个。 非常感谢提前。

5 个答案:

答案 0 :(得分:7)

作用域运算符::之前的名称必须始终是名称空间或类(或枚举)名称,并且名称空间名称不能相关。所以你不必告诉编译器这是一个类名。


我不仅仅是这样做,标准说([temp.res]部分):

  

在mem-initializer-id,base-specifier或elaborated-type-specifier中用作名称的限定名称被隐式假定为命名类型,而不使用typename关键字。在嵌套名称说明符中,它立即包含依赖于模板参数的嵌套名称说明符,隐式假定标识符或simple-template-id为name一种类型,不使用typename关键字。 [ 注意:   这些结构的语法不允许typename关键字。 - 结束说明]

T::T::Type::T::Type::Type::嵌套名称说明符,它们不需要标有typename。< / p>

本节显然可以,并且可以说应该包含豁免列表中的typedef声明的类型说明符。但请记住, type-specifiers 可能会变得非常复杂,尤其是在typedef声明中。现在,很可能在typedef 类型说明符中多次需要typename关键字,因此需要进行更多分析才能说服我typename永远不需要在typedef中。

typedef typename T::Type::Type Type中,T::Type::Type需要使用typename关键字,因为其嵌套名称说明符T::Type::)是一个依赖关键字名称和标准说(同一部分):

  

当qualified-id旨在引用不是当前实例化成员的类型(14.6.2.1)并且其嵌套名称说明符引用依赖类型时,它应该由关键字typename预先定义,形成一个typename-specifier。如果typename-specifier中的qualified-id不表示类型,则程序格式不正确。

答案 1 :(得分:3)

typename的要点是允许在实例化模板定义之前对其进行基本检查。在不知道名称是否是类型的情况下解析C ++是不可能的(a*b;表达式语句或指针符b的声明。)

在模板定义中,简单标识符的类别(类型或非类型)始终是已知的。但是一个合格的(依赖)名称不能 - 对于任意T,T :: x可以是。

因此,该语言允许您通过使用typename关键字告诉编译器限定名称表示类型。如果不这样做,编译器需要将其视为值类型。在任何一种情况下,误导编译器都是错误的。

即使在某些明显需要类型的情况下(例如在typedef中),该规则也适用。

只有完全限定名称才需要消除歧义 - typename A::B::C告诉您C是一种类型;没有必要知道有关A或B的任何信息来解析出现限定名称的上下文。

在您的示例(1)中,typename表示T::Type::Type是一种类型。在(2)中,您不能使用typename,因为T::Type::value不是类型。两个案例都没有说明T::Type,因为这是无关紧要的。 (虽然可以推断它必须是一种类型,因为否则你不能将::应用于它。)

我怀疑你的MSVC问题只是该编译器中的一个错误(因为它不能正确处理两阶段查找而臭名昭着),尽管我不得不承认我不是100%肯定。

答案 2 :(得分:2)

typedef typename T::Type::Type Type;  //(1)//legal?
enum {value = T::Type::Type::value }; //(2)//legal?

在(1)中你说T :: Type :: Type 是一个类型名称

在(2)中你没有提及T :: Type :: Type :: value ,默认情况下它将被解析为非类型

答案 3 :(得分:-1)

  

typedef typename T :: Type :: Type Type;   //(1)//合法?

我自己不明白这里需要typename。因为typedef只能应用于typename。也许C ++语法就是这样设计的。

  

enum {value = T :: Type :: Type :: value};   //(2)//合法?

不能使用typename因为,它应该是一个值。这是合乎逻辑的,当你写enum { value = ??? };时,???必须始终只是一个值。

答案 4 :(得分:-3)

typename指的是第一个依赖类型。在您的特定情况下:

typedef typename T::type1::type2 Type;

它引用T::type1,告诉它是一个从属名称(取决于模板参数T)。

对于常量值,您不需要typename,因为它是值 - 而不是类型。如果未定义该值,则会出现编译错误。

修改

struct S1
{
    enum { value = 42 };
};
template <class T> struct S2
{
    typedef S1 Type;
};
template <class T> struct S3
{
    typedef S2<T> Type; 
};
template <class T> struct S4
{
    typedef typename T::Type::Type Type;  //(1)//legal?
    enum {value = T::Type::Type::value }; //(2)//legal?
};

让我们慢慢地通过这个例子。此S4<S3<S2<S2<S1> > > >类型中发生的情况是:由于T为S3<S2<S2<S1> > >,因此typename T::Type会扩展为S2<S2<S1> >::Type,这是一种完整类型(不依赖于模板参数任何进一步)。因此,您不需要在第一个依赖类型名后使用typename。