The "Template non-type arguments" paragraph of the article „Template parameters and template arguments“ states:
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).
Emphasis is mine.
And below there is an example
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
Basing on the quoted statement, the lines with the word error in the comments should be compileable by c++ compilers supporting c++20, because a[2] and s.m are neither temporary objects nor string literals nor results of typeid nor predefined variables __func__ nor subobjects of one of the above. In fact, gcc 11.1 compiles the code without errors, but clang 12.0.0 compiles the code with errors.
Further, the draft N4835 declares the following in the paragraph 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
typeidexpression (7.6.1.7), or- a predefined
__func__variable (9.5.1).
and contains the mentioned example. Again emphasis is mine.
Taking into account this draft, clang works correctly and gcc does not, because a[2] and s.m are subobjects.
Digging deeper. The draft N4878 contains the following text in the paragraph 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
typeidexpression (7.6.1.8),- a predefined
__func__variable (9.5.1), or- a subobject (6.7.2) of one of the above.
and does not contain the example.
The quoted text in the beginning of this topic corresponds to the draft N4878 and accordingly the gcc works correctly and clang does not.
What does the C++20 standard say about usage of subojects as template non-type arguments? And which compiler does work in compliance with the standard?