;模板非类型参数";文章"模板参数和模板论点"的一段指出:
唯一的例外是引用的非类型模板参数或指针类型以及引用或指针的非静态数据成员类类型的非类型模板参数及其子对象中的类型(由于C++20(不能引用的地址
- 一个临时对象(包括在引用初始化期间创建的对象(
- 字符串文字
- CCD_ 1的结果
- 预定义的变量CCD_ 2
- 或上述对象之一的子对象(包括非静态类成员、基子对象或数组元素((自C++20以来(
重点是我的。
下面是一个的例子
template<int* p> class X {};
int a[10];
struct S
{
int m;
static int s;
} s;
X<&a[2]> x3; // error: address of array element
X<&s.m> x4; // error: address of non-static member
X<&s.s> x5; // ok: address of static member
X<&S::s> x6; // ok: address of static member
根据引用的语句,注释中带有单词error
的行应该可以由支持c++20的c++编译器编译,因为a[2]
和s.m
既不是临时对象,也不是字符串文字,也不是typeid
的结果,也不是预定义变量__func__
,也不是上述其中一个的子对象。事实上,gcc 11.1编译的代码没有错误,但clang 12.0.0编译的代码有错误。
此外,草案N4835在第13.4.2段中声明如下
非类型模板参数的模板参数应转换为模板参数类型的常量表达式(7.7(。对于引用或指针类型的非类型模板参数,或每个中引用或指针类型的非静态数据成员类类型或其子对象的非类型模板参数引用或指针值不应引用或是(分别(:
- 子对象(6.7.2(
- 临时对象(6.7.7(
- 字符串文字(5.13.5(,--(2.4(
typeid
表达式(7.6.1.7(的结果,或- 预定义的CCD_ 9变量(9.5.1(
并包含上述示例。我再次强调。
考虑到这个草案,clang工作正常,而gcc则不正常,因为typeid
0和s.m
是子对象。
挖得更深。草案N4878包含第13.4.3段中的以下文本
对于引用或指针类型的非类型模板参数,或者非类型中引用或指针类型的每个非静态数据成员类类型或其子对象的模板参数,引用或指针值不应引用或是(分别(:
- 一个临时对象(6.7.7(
- 字符串文字对象(5.13.5(
- CCD_ 12表达的结果(7.6.1.8(
- 预定义的
__func__
变量(9.5.1(,或- 上述其中一个的子对象(6.7.2(
并且不包含该示例。
本主题开头引用的文本对应于草案N4878,因此gcc工作正常,而clang则不正常。
C++20标准对使用子对象作为模板非类型参数有何规定?哪个编译器的工作符合标准?
作为P1907R1的一部分,措辞发生了变化,P1907R1被作为C++20的一部分采用。请注意,您引用的第一份草案N4835早于本次通过(该草案于2019年10月发布,本论文于下个月在2019年11月的贝尔法斯特会议上通过(。最接近C++20的草稿是N4861,您也可以方便地以html形式查看它
结果如下:
template<int* p> class X {};
int a[10];
struct S
{
int m;
static int s;
} s;
X<&a[2]> x3;
X<&s.m> x4;
是一个有效的C++20程序,因为a[2]
和s.m
都不是临时的、字符串文字、typeid
表达式的结果或__func__
中任何一个的子对象。
cppreference示例已经更新以反映这一点,其中的注释现在为:
X<&a[2]> x3; // error (until C++20): address of array element
X<&s.m> x4; // error (until C++20): address of non-static member