依赖名称的模板消除歧义

Jod*_*ebo 8 c++

我通过编译以下示例遇到了以下问题:

template <int N>
class Matrix {
public:
    template <int Idx>
    int head() {
        return Idx;
    }
};

template <typename T>
class Test {
    static constexpr int RayDim = 3;
public:
    int func() const {
        Matrix<RayDim> yF;
        return yF.head<1>();
        //        ^ is template keyword required here?
    }
};

struct Empty {};

void test() {
    Test<Empty> t;
}
Run Code Online (Sandbox Code Playgroud)

链接到编译器资源管理器:https : //godbolt.org/z/js4XaP

该代码使用GCC 9.2和MSVC 19.22进行编译,但不能使用clang 9.0.0进行编译。Clang指出需要template关键字。如果static constexpr int RayDim = 3;移入int func() constclang接受它。

如代码块中的注释所述,是否需要template关键字yF.head<1>()

asc*_*ler 1

这里不应该需要关键字template,因此 clang 拒绝程序是不正确的。

C++17 草案 N4659 和当前链接的 C++20 草案的所有 C++ 标准章节和段落编号以及下面的引用均相同。

命名成员模板时要求在or或标记template之后位于[temp.names]/4中。该段落首先列出不允许使用该关键字的情况,然后列出该关键字是可选的且没有区别的情况,然后:.->::

在所有其他上下文中,在命名未知专业化 ( [temp.dep.type] ) 的成员的模板专业化时,成员模板名称应以关键字 为前缀template

“未知专业化的成员”是不是“当前实例化”的依赖类型的成员。所以问题是是否Matrix<RayDim>是依赖类型。为此,我们查看 [temp.dep.type]/9:

一个类型是依赖的,如果它是

  • 模板参数,
  • 未知专业的成员,
  • 作为当前实例化的依赖成员的嵌套类或枚举,
  • 一个 cv 限定类型,其中 cv 不限定类型是从属的,
  • 由任何依赖类型构造的复合类型,
  • 其元素类型是相关的或其边界(如果有)是值相关的数组类型,
  • 其异常规范与值相关的函数类型,
  • simple -template-id,其中模板名称是模板参数,或者任何模板参数是依赖类型或者依赖于类型或值的表达式或者是包扩展,或者
  • decltype(由表达式表示),其中表达式与类型相关。

Matrix<RayDim>显然不是模板参数、任何类型的成员、cv 限定的、数组类型、函数类型或由 指定的decltype。它是一种复合类型,但它仅使用模板名称和表达式,因此不是从任何其他类型构造的。

这就剩下simple-template-id 的情况了。模板名称Matrix不是模板参数。模板参数RayDim是一个表达式,所以现在看看它是类型相关的还是值相关的。

“类型相关”在[temp.dep.expr]中定义。只有第 3 段可以适用于单独的标识符,例如RayDim

如果id表达式包含,则它是类型相关的

  • 通过名称查找与使用依赖类型声明的一个或多个声明关联的标识符
  • 通过名称查找与使用包含占位符类型的类型声明的非类型模板参数关联标识符,
  • 通过名称查找与使用包含占位符类型 ([dcl.spec.auto]) 的类型声明的变量关联的标识符,其中初始值设定项与类型相关,
  • 通过名称查找与当前实例化的成员函数的一个或多个声明相关联的标识符,该成员函数使用包含占位符类型的返回类型进行声明
  • 通过名称查找与结构化绑定声明关联的标识符,该声明的大括号或等于初始化程序与类型相关,
  • 标识符__func__([dcl.fct.def.general]),其中任何封闭函数都是模板、类模板的成员或通用 lambda,
  • 依赖的模板ID ,
  • 指定依赖类型的转换函数 ID,或者
  • 嵌套名称说明符限定 ID,用于命名未知专业化的成员;

T或者,如果它命名当前实例化的依赖成员,该成员是某些类型“未知边界数组”的静态数据成员T([temp.static])。

RayDim当然不包含任何__func__template-idconversion-function-idnested-name-specifierQualified-id。名称查找查找类模板的静态成员声明。该声明RayDim当然不是模板参数、成员函数或结构化绑定声明,并且其类型const int当然不是依赖类型或数组类型,并且不包含占位符类型。所以RayDim不依赖于类型。

“值相关”在[temp.dep.constexpr]中定义。唯一可以应用于单独标识符的情况RayDim是第 2 段中的情况:

如果满足以下条件,则id表达式是值相关的:

  • 它是依赖于类型的,
  • 它是非类型模板参数的名称,
  • 它命名一个静态数据成员,该成员是当前实例化的依赖成员,并且未在成员声明符中初始化,
  • 它命名一个静态成员函数,该函数是当前实例化的依赖成员,或者
  • 它是一个具有文字类型的常量,并使用与值相关的表达式进行初始化。

从上面来看,RayDim不依赖于类型。它当然不是模板参数或成员函数。它是当前实例化的静态数据成员和依赖成员,但它是在成员声明符中初始化的。也就是说,“ = 3”出现在类定义中,而不是单独的成员定义中。它是一个具有文字类型的常量,但其初始值设定项3不依赖于值。

所以RayDim不依赖于值或类型。因此Matrix<RayDim>不是依赖类型,yF.head不是未知实例化的成员,并且template前面的关键字head是可选的,不是必需的。(这是允许的,因为它不在“仅类型上下文”中,并且head实际上命名了成员模板。)