1. Risposta breve: Funziona a prescindere che sia dichiarato constexpr
, perché si sta definendo un oggetto con durata dell'archiviazione statica (che non è una stringa letterale - memorizza una copia dei contenuti di uno) e il suo indirizzo è un'espressione costante. Per quanto riguarda il collegamento, str2
ha un collegamento interno, ma va bene - il suo indirizzo può essere utilizzato come argomento modello non di tipo.
Risposta lunga:
In C++ 11 e 14, [14.3.2p1] dice quanto segue:
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;
[...]
Quindi, è possibile utilizzare l'indirizzo di un oggetto con la durata di stoccaggio statico, ma l'oggetto deve essere identificato da un nome con linkage (interno o esterno) e il modo in cui si sta esprimendo tale indirizzo è limitato. (I valori letterali delle stringhe non sono nomi e non hanno linkage.)
In breve, anche char str1[] = "Test 1";
funziona. static char str1[] = "Test 1";
va bene anche; GCC 5.1.0 lo rifiuta, ma penso che sia un bug; Clang 3.6.0 lo accetta.
A proposito di linkage 's str2
, C++ 11 e 14 [3.5p3] dice:
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
or constexpr
and neither explicitly declared extern
nor previously declared to have external linkage;
[...]
N4431 è cambiato un po', a seguito di DR 1686, a:
- a variable of non-volatile const-qualified type that is neither explicitly declared
extern
nor previously declared to have external linkage;
che riflette il fatto che constexpr
implica la costante qualificazione per gli oggetti.
2. Risposta breve: per C++ 11 e 14, vedi sopra; per bozza C++ 1z, str3
non è un'espressione costante, poiché il puntatore stesso non è constexpr
ed è anche l'indirizzo di una stringa letterale. str4
è costante, ma è pur sempre un indirizzo di una stringa letterale.
Risposta lunga:
Nella bozza di lavoro corrente, N4431, i vincoli non-argomenti di tipo di modello è stato rilassato. [14.3.2p1] ora dice:
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).
E quelle sono tutte le restrizioni. La parte di espressione costante convertita è piuttosto importante; la definizione completa è long, ma una parte rilevante per il nostro caso è che l'indirizzo di un oggetto con durata di memorizzazione statica è una tale espressione.
inoltre rilevante è che, secondo [5.20p2.7], un Ivalue a rvalue conversione applicato a
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
soddisfa anche le condizioni per essere un'espressione costante. Ciò ci consente di utilizzare alcune variabili puntatore constexpr
come argomenti modello non di tipo. (Si noti che la semplice dichiarazione di una variabile const
non è sufficiente, in quanto può essere inizializzata con un'espressione non costante.)
Quindi, qualcosa come constexpr const char* str3 = str1;
va bene. È accettato da Clang 3.6.0 in modalità C++ 1z (e rifiutato in modalità C++ 14); GCC 5.1.0 lo rifiuta ancora - sembra che non abbia ancora implementato le regole aggiornate.
Ancora, cosa c'è di sbagliato con stringhe letterali? Ecco il problema (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.
L'attuazione è permesso di fare un sacco di cose con le stringhe: mix, match, renderli sovrappongono (interamente o parzialmente), fare 7 copie dal stessa unità di traduzione - qualunque cosa. Ciò rende l'indirizzo di una stringa letterale inutilizzabile come argomento modello non di tipo.
Ho visto una domanda simile recentemente, che mi ha chiesto perché una dichiarazione "extern" non può essere utilizzata per la deduzione del valore temporale compilato. Probabilmente è perché il linker gestisce effettivamente 'extern', e il compilatore non può usare quelle informazioni quando il codice è compilato. –
@ πάνταῥεῖ Ciò che è strano è che in (1) io uso una definizione di 'extern', che funziona. – vsoftco
ciò che rende '(3)' diverso dagli altri due è che ti piacerebbe che l'indirizzo contenesse "dentro" la variabile, e non l'indirizzo * della * stessa variabile. Sono attualmente sul mio telefono e in tal modo potrei tornare a questa domanda in un secondo momento per fornire una risposta. –