我通过编译以下示例遇到了以下问题:
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;
}
链接到编译器资源管理器:
https://godbolt.org/z/js4XaP
该代码使用GCC 9.2和MSVC 19.22进行编译,但不能使用clang 9.0.0进行编译。 Clang指出需要template关键字。如果将
static constexpr int RayDim = 3;
移入int func() const
,则clang接受它。如代码块中的注释所述,
yF.head<1>()
是否需要template关键字? 最佳答案
此处不需要template
关键字,因此clang不正确,无法拒绝该程序。
对于C++ 17草案N4659和当前链接的C++ 20草案,下面的所有C++标准部分和段落编号以及引号均相同。
命名成员模板时,在template
或.
或->
token 之后需要::
在[temp.names]/4中。该段首先列出了不允许使用关键字的情况,然后列出了可选的且没有区别的情况,然后:
“未知特化的成员”是非“当前实例化”的从属类型的成员。因此,问题是Matrix<RayDim>
是否为从属类型。为此,我们来看[temp.dep.type]/9:
Matrix<RayDim>
显然不是模板参数,任何类型的成员,cv限定,数组类型,函数类型或不是decltype
指定的。它是一种复合类型,但是它仅使用模板名称和表达式,因此不会从任何其他类型构造。
剩下的就是简单模板ID的情况。模板名称Matrix
不是模板参数。模板参数RayDim
是一个表达式,因此现在来看它是类型依赖还是值依赖。
在[temp.dep.expr]中定义了“取决于类型”。只有第3段可以适用于像RayDim
这样的单独标识符:
RayDim
当然不包含任何__func__
,template-id,conversion-function-id,nested-name-specifier或qualified-id。名称查找可找到类模板的静态成员声明。 RayDim
的声明当然不是模板参数,成员函数或结构化绑定(bind)声明,并且其类型const int
肯定不是从属类型或数组类型,并且不包含占位符类型。因此,RayDim
与类型无关。
在[temp.dep.constexpr]中定义了“依赖于值”。第2段中仅适用于像RayDim
这样的单独标识符的情况:
从上面看,RayDim
与类型无关。它当然不是模板参数或成员函数。它是当前实例的静态数据成员和从属成员,但已在成员声明器中初始化。也就是说,“= 3
”出现在类定义内,而不是在单独的成员定义内。它是具有文字类型的常量,但是其初始值设定项3
与值无关。
因此,RayDim
不依赖于值或类型。因此,Matrix<RayDim>
不是从属类型,yF.head
不是未知实例化的成员,并且template
之前的head
关键字是可选的,不是必需的。 (这是允许的,因为它不在“仅类型上下文”中,并且head
实际上确实命名了成员模板。)