ISO/IEC JTC1 SC22 WG21
N4198
Richard Smith
richard@metafoo.co.uk
2014-10-06
C++ supports the following kinds of non-type template parameters and non-type template arguments (see [temp.param]/4 and [temp.arg.nontype]/1,5):
Parameter type | Argument syntax |
---|---|
Integral or enumeration | Arbitrary constant expression |
Pointer type | Exact syntax &entity, array, function, referring to a static storage duration object or function with linkage, or arbitrary constant expression that evaluates to a null pointer |
Reference type | Exact syntax object, function, referring to a static storage duration object or function with linkage |
Pointer to member | Exact syntax &X::y, or arbitrary constant expression that evaluates to a null pointer-to-member |
std::nullptr_t | Arbitrary constant expression |
The syntactic restrictions for pointers, references, and pointers to members are awkward and prevent reasonable refactorings. For instance:
template<int *p> struct A {}; int n; A<&n> a; // ok constexpr int *p() { return &n; } A<p()> b; // error
The same refactorings work fine for integral types. They also work fine if the template argument happens to evaluate to a null pointer:
constexpr int *q() { return nullptr; } A<q()> c; // ok!
The historical reason for the restriction was most likely that C++ previously did not have a sufficiently strong specification for constant expressions of pointer, reference, or pointer-to-member type. However, that is no longer the case. The status quo is that an implementation is required to evaluate such a template argument, but must then discard the result if it turns out to not be null.
In addition to the above, the restriction to entities with linkage is an artifact of exported templates, and could have been removed when the linkage restrictions on template type parameters were removed.
This paper proposes revising the above table as follows:
Parameter type | Argument syntax |
---|---|
Integral or enumeration | Arbitrary constant expression |
Pointer type | Arbitrary constant expression that evaluates to the address of a complete object with static storage duration, a function, or a null pointer |
Reference type | Arbitrary constant expression that evaluates to a glvalue referring to a complete object with static storage duration or to a function. |
Pointer to member | Arbitrary constant expression |
std::nullptr_t | Arbitrary constant expression |
The restriction that the constant expression must name a complete object is retained to avoid aliasing problems with pointers to subobjects:
struct A { int x, y; } a; template<int*> struct Z; using B = Z<&a.x + 1>; using C = Z<&a.y>; // Are B and C the same type?
Additionally, the (previously implicit) restriction against string literals, temporaries, and typeid expressions is retained, because those expressions are not required to produce the same object each time they are evaluated.
This paper also proposes to allow the conversions permitted in converted constant expressions for all non-type template arguments. In particular, this allows lvalue-to-rvalue conversions (supporting constexpr globals of the type of the template parameter) and user-defined conversions (supporting arguments of literal class type with appropriate user-defined constexpr conversion operators). Such conversions are already permitted for non-type template parameters of integral or enumeration type.
Change in 5.19 (expr.const) paragraph 3:
[...] A converted constant expression of type T is an expression, implicitly converted toDrafting note: previously, a converted constant expression could only be of integral or enumeration type, so these conversions do not change any existing uses of the term.a prvalue oftype T, where the converted expression is acoreconstant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions (4.1), array-to-pointer conversions (4.2), function-to-pointer conversions (4.3), qualification conversions (4.4), integral promotions (4.5),andintegral conversions (4.7) other than narrowing conversions (8.5.4), and null pointer conversions (4.10) and null member pointer conversions (4.11) from std::nullptr_t, and where the reference binding (if any) binds directly. [ Note: such expressions may be used in new expressions (5.3.4), as case expressions (6.4.2), as enumerator initializers if the underlying type is fixed (7.2), as array bounds (8.3.4), and asintegral or enumerationnon-type template arguments (14.3). — end note ]
Change in 14.3.2 (temp.arg.nontype) paragraph 1 and remove the bullets:
A template-argument for a non-type, non-templatetemplate-parameter shall beone of:For a non-type template-parameter of reference type or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
for a non-type template-parameter of integral or enumeration type,a converted constant expression (5.19) of the type of the template-parameter.; orthe name of a non-type template-parameter; ora constant expression (5.19) that designates the address of a complete objectwith static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, where the id-expression is the name of an object or function, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; ora constant expression that evaluates to a null pointer value (4.10); ora constant expression that evaluates to a null member pointer value (4.11); ora pointer to member expressed as described in 5.3.1; ora constant expression of type std::nullptr_t.[ Note: If the template-argument represents a set of overloaded functions (or a pointer or member pointer to such), the matching function is selected from the set (13.4). ]
- a subobject (1.8),
- a temporary object (12.2),
- a string literal (2.14.5),
- the result of a typeid expression (5.2.8), or
- a predefined __func__ variable (8.4.1).
Change in 14.3.2 (temp.arg.nontype) paragraph 2:
[ Note:A string literal (2.14.5) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.String literals are not acceptable template-arguments. [ Example: ... ] ]
Change in 14.3.2 (temp.arg.nontype) paragraph 3:
[ Note: Addresses of array elements and names or addresses of non-static class members are not acceptable template-arguments. [ 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; //] ]error: &S::s must be usedOK: address of static member X<&S::s> x6; // OK: address of static member
Change in 14.3.2 (temp.arg.nontype) paragraph 4:
[ Note: Temporaries, unnamed lvalues, and named lvalues with no linkageare not acceptable template-arguments when the corresponding template-parameter has reference type. [ Example: ... ] ]
Delete all of 14.3.2 (temp.arg.nontype) paragraph 5 other than its example and move it to before paragraph 2:
The following conversions are performed on each expression used as a non-type template-argument. If a non-type template-argument cannot be converted to the type of the corresponding template-parameter then the program is ill-formed.[ Example: ... ]
- ...
Change in 14.4 (temp.type) paragraph 1:
Two template-ids refer to the same class, function, or variable if[ Example: ... ]
- their template-names, operator-function-ids, or literal-operator-ids refer to the same template and
- their corresponding type template-arguments are the same type and
- their corresponding non-type template arguments of integral or enumeration type have identical values and
- their corresponding non-type template-arguments of pointer type refer to the same
externalobject or function or are both the null pointer value and- their corresponding non-type template-arguments of pointer-to-member type refer to the same class member or are both the null member pointer value and
- their corresponding non-type template-arguments of reference type refer to the same
externalobject or function and- their corresponding template template-arguments refer to the same template.