通常,typename
用于消除标识符可能引用类型的情况之间的歧义,或者可以引用其他内容:
template<class T>
void foo(typename T::type value) {
// ...
}
typename
be used when an identifier is already a type?1.如果已经有一个具有此名称的课程,可以使用它吗?
class MyClass{};
void foo(typename MyClass value) {}
2.它可以与声明为类型的模板参数一起使用吗?
template<class T>
void foo(typename T value) {}
3.它可以用于明确类型的内部类吗?
class A {
public:
class B {};
};
// Compiles; no typename necessary
void foo(A::B value) {}
// This compiles too on gcc and msvc
void bar(typename A::B value) {}
案例1:MSVC认为这样做; gcc和clang抛出一个错误
案例2:MSVC认为这样可以; gcc和clang抛出一个错误
案例3:A::B
明确地是一种类型,但gcc和clang现在允许使用typename
。
关键字typename
只允许由C ++ syntax引入template type parameter或before a qualified name,即包含::
标记的东西。
所以你的#1和#2是不正确的,因为MyClass
和T
是不合格的名字,不包含任何::
。
在限定名称之前,typename
令牌是:
class
, struct
, or union
keywords之前的语法不允许;在这些上下文中,限定名称始终被视为类型C ++ 17 [temp.res] / 3,5,6:
当qualified-id旨在引用不是当前实例化成员的类型且其嵌套名称说明符引用依赖类型时,它应以关键字
typename
作为前缀,形成typename-specifier。 ...在class-or-decltype(Clause [class.derived])或elaborated-type-specifier中用作名称的限定名称被隐式假定为命名类型,而不使用
typename
关键字。在直接包含依赖于模板参数的嵌套名称说明符的嵌套名称说明符中,隐式假定标识符或simple-template-id命名类型,而不使用typename
关键字。 [注意:这些结构的语法不允许使用typename
关键字。 - 结束说明]如果对于给定的一组模板参数,实例化了模板的特化,该特化引用了表示类型或类模板的qualified-id,而qualified-id引用了未知特化的成员,则为 - id应以
typename
为前缀,或者在上下文中隐含地命名类型的上下文中使用。
所以你的#3格式正确,即使名称不依赖于模板参数,甚至不在模板声明中。
注意C ++ 20将添加many more contexts,其中typename
是可选的,即使具有依赖名称,因为可以从上下文中明确地确定该名称只能表示类型。
用法:
- 在模板声明中,typename可以用作类的替代,以声明类型模板参数和模板模板参数(自C ++ 17起)。
- 在声明或模板定义中,typename可用于声明依赖名称是一种类型。
- 满足类型要求的要求(自C ++ 20以来)
所以我会说你没有保证案例1和案例2都会编译。因为它们不属于这三种使用情况中的任何一种。
对于案例3,让我们看看cppreference对此有何评论:
关键字
typename
只能在限定名称(例如T :: x)之前以这种方式使用,但名称不需要依赖。关键字typename只能在模板声明和定义中使用,并且只能在可以使用从属名称的上下文中使用。这排除了显式特化声明和显式实例化声明。(直到C ++ 11)
关键字typename甚至可以在模板之外使用。 (自C ++ 11以来)
因为在您的示例中没有模板,所以应保证案例3仅在C ++ 11之后才能工作
事实上,测试程序使用g ++ -std = c ++ 11编译好,但是在没有-std = c ++ 11的情况下发出了此警告
警告:'typename'出现在模板之外[-Wc ++ 11-extensions]