c++ - Template tricks with const char* as a non-type parameter -
i aware passing directly const char*
template non-type parameter erroneous, since 2 identical string literals defined in 2 different translation units may have different addresses (although of time compilers use same address). there trick 1 may use, see code below:
#include <iostream> template<const char* msg> void display() { std::cout << msg << std::endl; } // need have external linkage // there no multiple definitions extern const char str1[] = "test 1"; // (1) // why constexpr enough? have external linkage? constexpr char str2[] = "test 2"; // (2) // why doesn't work? extern const char* str3 = "test 3"; // (3) doesn't work // using c_ptr_char = const char* const; // (4) doesn't work either extern constexpr c_ptr_char str4 = "test 4"; int main() { display<str1>(); // (1') display<str2>(); // (2') // display<str3>(); // (3') doesn't compile //display<str4>(); // (4') doesn't compile }
basically in (1) declare , define array external linkage, can used template parameter in (1'). understand well. however, don't understand:
why
constexpr
version (2) works?constexpr
have external linkage? if not, defining same string literal in different translation unit may lead duplicate template instantiation.why (3) , (4) don't work? seems reasonable me, compiler doesn't believe so:
error: 'str3' not valid template argument because 'str3' variable, not address of variable
1. short answer: works irrespective of being declared constexpr
, because you're defining object static storage duration (that not string literal - stores copy of contents of one), , address constant expression. regarding linkage, str2
has internal linkage, that's fine - address can used non-type template argument.
long answer:
in c++11 , 14, [14.3.2p1] says following:
a template-argument non-type, non-template template-parameter shall 1 of:
[...]
- a constant expression (5.19) designates address of complete object static storage duration , external or internal linkage or function external or internal linkage, including function templates , function template-ids excluding non-static class members, expressed (ignoring parentheses)
&
id-expression, id-expression name of object or function, except&
may omitted if name refers function or array , shall omitted if corresponding template-parameter reference;[...]
so, can use address of object static storage duration, object has identified name linkage (internal or external), , way you're expressing address restricted. (string literals not names , don't have linkage.)
in short, char str1[] = "test 1";
works. static char str1[] = "test 1";
fine well; gcc 5.1.0 rejects it, think that's bug; clang 3.6.0 accepts it.
about str2
's linkage, c++11 , 14 [3.5p3] says:
a name having namespace scope (3.3.6) has internal linkage if name of
[...]
- a non-volatile variable explicitly declared
const
orconstexpr
, neither explicitly declaredextern
nor declared have external linkage;[...]
n4431 has changed slightly, result of dr 1686, to:
- a variable of non-volatile const-qualified type neither explicitly declared
extern
nor declared have external linkage;
reflecting fact constexpr
implies const-qualification objects.
2. short answer: c++11 , 14, see above; draft c++1z, str3
not constant expression, pointer not constexpr
, , it's address of string literal. str4
constant, still address of string literal.
long answer:
in current working draft, n4431, constraints on non-type template arguments have been relaxed. [14.3.2p1] says:
a template-argument non-type template-parameter shall converted constant expression (5.20) of type of template-parameter. non-type template-parameter of reference or pointer type, value of constant expression shall not refer (or pointer type, shall not address of):
- a subobject (1.8),
- a temporary object (12.2),
- a string literal (2.13.5),
- the result of
typeid
expression (5.2.8), or- a predefined
__func__
variable (8.4.1).
and restrictions. converted constant expression part pretty important; full definition long, 1 part relevant our case address of object static storage duration such expression.
also relevant that, according [5.20p2.7], an lvalue-to-rvalue conversion applied
a non-volatile glvalue refers non-volatile object defined
constexpr
, or refers non-mutable sub-object of such object
also satisfies conditions being constant expression. allows use constexpr
pointer variables non-type template arguments. (note declaring variable const
not enough, can initialized non-constant expression.)
so, constexpr const char* str3 = str1;
fine. it's accepted clang 3.6.0 in c++1z mode (and rejected in c++14 mode); gcc 5.1.0 still rejects - looks hasn't implemented updated rules yet.
still, what's wrong string literals? here's problem (n4431 [2.13.5p16]):
evaluating string-literal results in string literal object static storage duration, initialized given characters specified above. whether string literals distinct (that is, stored in nonoverlapping objects) , whether successive evaluations of string-literal yield same or different object unspecified.
an implementation allowed lots of things string literals: mix, match, make them overlap (entirely or partially), make 7 copies same translation unit - whatever. makes address of string literal unusable non-type template argument.
Comments
Post a Comment