template<typename T, T nontype_param>
class C {};
int a[10];
C<int *, &a[0]> err3;
The code above will throw an error when the standard of c++ is lower than 17, while it will be ok when the standard is 17. Could someone explain the reasons to me?
According the information on the cppreference:
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
or a subobject (including non-static class member, base subobject, or array element) of one of the above(since C++20).
(The words in cppreference are formatted, and I am unsure how to correctly paste it here in markdown. If you are confused by my reference, please click the link and find the section to read. )
I think using the address of the first element of the array as template non-type argument should not pass even in c++ 17.
tldr;
The answer depends on which standard c++ version you're using as explained below. In C++20 the program is well-formed while in C++17 it is ill-formed. The important thing to note is that a
has static storage duration and that c++20 allows the address of certain subobjects(as opposed to C++17 which didn't allow "any" subobject address) to be template arguments.
The program is well-formed in c++20 as per temp.arg.nontype:
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
- a string literal object
- the result of a
typeid
expression- a predefined func variable or
- a subobject (6.7.2) of one of the above.
(emphasis mine)
Since &a[0]
is neither of the above things mentioned in the list, the program is well-formed.
Also note that the template argument must be a converted constant expression which is true here. From temp.arg:
A template-argument for a non-type template-parameter shall be a converted constant expression ([expr.const]) of the type of the template-parameter. [ Note: If the template-argument is an overload set (or the address of such, including forming a pointer-to-member), the matching function is selected from the set ([over.over]). — end note ]
Note a converted constant expression should also be a constant expression as per expr.const:
A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only : * ...
Now we move onto constant expression
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:
- if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a non-immediate function, or a null pointer value,
In C++17, the program is ill-formed as per temp.arg:
A template-argument for a non-type template-parameter shall be a converted constant expression of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
- a subobject,
(emphasis mine)