什么时候可以将"typename"与明确引用类型的标识符一起使用?

When can `typename` be used with identifiers that unambiguously refer to a type?

本文关键字:quot 标识符 一起 引用类型 typename 什么时候      更新时间:2023-10-16

通常,typename用于消除标识符可能引用类型或可能引用其他内容的情况之间的歧义:

template<class T>
void foo(typename T::type value) {
// ...
}

当标识符已经是一种类型时,何时可以使用typename

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++语法允许引入模板类型参数,或在限定名称(即包含::标记的内容)之前引入。

所以你的 #1 和 #2 格式不正确,因为MyClassT是非限定名称,不包含任何::

在限定名称之前,typename令牌为:

语法不允许在类
  • 定义头部的基类名之前使用,也不允许与classstructunion关键字组合使用;在这些上下文中,限定名始终被视为类型
  • 否则需要,如果限定名称是相关名称并且是未知专业化的成员
  • 否则是可选的,无论是否在模板声明中

C++17 [温度分辨率]/3,5,6:

限定 id旨在引用不是当前实例化成员的类型并且其嵌套名称说明符引用依赖类型时,它应以关键字typename为前缀,形成类型名称说明符

类或 decltype(子句 [class.derived])中用作名称的限定名称或详细类型说明符隐式假定为命名类型,而不使用typename关键字。在紧随其后的嵌套名称说明符中,隐式假定标识符简单模板 ID用于命名类型,而不使用typename关键字。[注意:这些构造的语法不允许使用typename关键字。

如果对于一组给定的模板参数,实例化了模板的专用化,该专用化引用表示类型或类模板的限定ID,并且限定id 引用未知专用化的成员,则限定 id应以typename为前缀,或者应在它隐式命名如上所述类型的上下文中使用。

因此,您的 #3 格式正确,即使名称不依赖于模板参数,甚至不在模板声明中。

注意 C++20 将添加更多上下文,其中即使使用依赖名称typename也是可选的,因为可以从上下文中明确确定名称只能表示类型。

来自 cpp首选项

用法:

  • 在模板声明中,typename 可以用作类的替代项来声明类型模板参数和模板模板参数(自 C++17 起)。
  • 在模板的声明或定义中,typename 可用于声明依赖名称是类型。
  • 内部对类型要求的要求(自 C++20 起)

所以我想说的是,你无法保证案例 1 和案例 2 会编译。因为它们不属于这三个用例中的任何一个。

对于案例 3,让我们看看 cppPreferred 对此有何看法:

关键字typename只能在限定名称(例如 T::x)之前以这种方式使用,但名称不必依赖。

关键字 typename 只能在模板声明和定义中使用,并且只能在可以使用依赖名称的上下文中使用。这不包括显式专用化声明和显式实例化声明。(至C++11)

关键字类型名称甚至可以在模板之外使用。(自C++11起)

因此,由于您的示例中没有模板,因此应保证案例 3 仅在 C++11 之后才能正常工作

确实有一个使用 g++ -std=c++11编译正常的测试程序,但在没有-std=c++11的情况下发出此警告

警告:"类型名称"出现在模板外部 [-WC++11-扩展]