Why using the address of the first element of the array as template non-type argument is possible in c++ 17

153 Views Asked by At
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.

1

There are 1 best solutions below

9
user12002570 On BEST ANSWER

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.

C++20

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,

C++17

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)