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
typeid
expression (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
typeid
expression (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?
Non-type template arguments are normally used to initialize a class or to specify the sizes of class members. For non-type integral arguments, the instance argument matches the corresponding template parameter as long as the instance argument has a value and sign appropriate to the parameter type.
In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument. A non-type parameter can be any of the following types: An integral type. An enumeration type.
What can be passed by non-type template parameters during compile time? Explanation: Non-type template parameters provide the ability to pass a constant expression at compile time. The constant expression may also be an address of a function, object or static class member.
The wording changed as part of 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.
As a result, the following:
template<int* p> class X {};
int a[10];
struct S
{
int m;
static int s;
} s;
X<&a[2]> x3;
X<&s.m> x4;
is a valid C++20 program, since neither a[2]
nor s.m
are subobjects of any of: a temporary, a string literal, the result of a typeid
expression, or __func__
.
The cppreference example has already been updated to reflect this, where the comments now read:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With