C++20 标准对将子对象用作模板非类型参数有何规定?

What does the C++20 standard say about usage of subojects as template non-type arguments?

“模板参数和模板参数”一文的“Template non-type arguments”段落指出:

The only exceptions are that non-type template parameters of reference or pointer type and non-static data members of reference or pointer type in a non-type template parameter of class type and its subobjects (since C++20) cannot refer to/be the address of

  • a temporary object (including one created during reference initialization);
  • a string literal;
  • the result of typeid;
  • the predefined variable __func__;
  • or a subobject (including non-static class member, base subobject, or array element) of one of the above (since 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 编译 code without errors, but clang 12.0.0 compiles the code 有错误。

此外,draft N4835 在第 13.4.2 段中声明如下

A template-argument for a non-type template-parameter shall be a converted constant expression (7.7) of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):

  • a subobject (6.7.2),
  • a temporary object (6.7.7),
  • a string literal (5.13.5),—(2.4)the result of a typeid expression (7.6.1.7), or
  • a predefined __func__ variable (9.5.1).

并包含上述示例。再次强调是我的。

考虑到这个草案,clang 可以正常工作而 gcc 不能,因为 a[2]s.m 是子对象。

深入挖掘。 draft N4878段落13.4.3

中包含以下文字

For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):

  • a temporary object (6.7.7),
  • a string literal object (5.13.5),
  • the result of a typeid expression (7.6.1.8),
  • a predefined __func__ variable (9.5.1), or
  • a subobject (6.7.2) of one of the above.

且不包含示例。

本主题开头引用的文本对应于草案 N4878,因此 gcc 可以正常工作,而 clang 不能。

C++20 标准对将子对象用作模板非类型参数有何规定?哪个编译器确实符合标准?

作为 P1907R1, which was adopted as part of C++20. Note that the first draft you cited - N4835 - predates this adoption (that draft was published Oct 2019, and this paper was adopted the following month at the Belfast meeting in Nov 2019). The closest draft to C++20 is N4861, which you can also conveniently view in html form 的一部分更改了措辞。

结果如下:

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