Template tricks with const char* as a non-type parameter
1. Short answer: It works irrespective of it being declared constexpr
, because you're defining an object with static storage duration (that is not a string literal - it stores a copy of the contents of one), and its address is a constant expression. Regarding linkage, str2
has internal linkage, but that's fine - its address can be used as a non-type template argument.
Long answer:
In C++11 and 14, [14.3.2p1] says the following:
A template-argument for a non-type, non-template template-parameter shall be one of:
[...]
- a constant expression (5.19) that designates the address of a complete object with 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;[...]
So, you can use the address of an object with static storage duration, but the object has to be identified by a name with linkage (internal or external), and the way you're expressing that address is restricted. (String literals are not names and don't have linkage.)
In short, even char str1[] = "Test 1";
works. static char str1[] = "Test 1";
is fine as well; GCC 5.1.0 rejects it, but I think that's a bug; Clang 3.6.0 accepts it.
About str2
's linkage, C++11 and 14 [3.5p3] says:
A name having namespace scope (3.3.6) has internal linkage if it is the name of
[...]
- a non-volatile variable that is explicitly declared
const
orconstexpr
and neither explicitly declaredextern
nor previously declared to have external linkage;[...]
N4431 has changed that slightly, as a result of DR 1686, to:
- a variable of non-volatile const-qualified type that is neither explicitly declared
extern
nor previously declared to have external linkage;
reflecting the fact that constexpr
implies const-qualification for objects.
2. Short answer: For C++11 and 14, see above; for draft C++1z, str3
is not a constant expression, as the pointer itself is not constexpr
, and it's also the address of a string literal. str4
is constant, but still an address of a string literal.
Long answer:
In the current working draft, N4431, the constraints on non-type template arguments have been relaxed. [14.3.2p1] now says:
A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) 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 (1.8),
- a temporary object (12.2),
- a string literal (2.13.5),
- the result of a
typeid
expression (5.2.8), or- a predefined
__func__
variable (8.4.1).
And those are all the restrictions. The converted constant expression part is pretty important; the full definition is long, but one part relevant to our case is that the address of an object with static storage duration is such an expression.
Also relevant is that, according to [5.20p2.7], an lvalue-to-rvalue conversion applied to
a non-volatile glvalue that refers to a non-volatile object defined with
constexpr
, or that refers to a non-mutable sub-object of such an object
also satisfies the conditions for being a constant expression. This allows us to use some constexpr
pointer variables as non-type template arguments. (Note that simply declaring a variable const
is not enough, as it can be initialized with a non-constant expression.)
So, something like constexpr const char* str3 = str1;
is fine. It's accepted by Clang 3.6.0 in C++1z mode (and rejected in C++14 mode); GCC 5.1.0 still rejects it - it looks like it hasn't implemented the updated rules yet.
Still, what's wrong with string literals? Here's the problem (N4431 [2.13.5p16]):
Evaluating a string-literal results in a string literal object with static storage duration, initialized from the given characters as specified above. Whether all string literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.
An implementation is allowed to do lots of things with string literals: mix, match, make them overlap (entirely or partially), make 7 copies from the same translation unit - whatever. That makes the address of a string literal unusable as a non-type template argument.