C ++ 17 introduce variabili inline
C ++ 17 risolve questo problema per le constexpr static
variabili membro che richiedono una definizione fuori linea se è stato usato odr. Vedi la seconda metà di questa risposta per i dettagli pre-C ++ 17.
Proposta P0386 Variabili incorporate introduce la possibilità di applicare lo inline
specificatore alle variabili. In particolare, questo caso constexpr
implica inline
variabili membro statiche. La proposta dice:
L'identificatore inline può essere applicato sia alle variabili che alle funzioni. Una variabile dichiarata in linea ha la stessa semantica di una funzione dichiarata in linea: può essere definita, in modo identico, in più unità di traduzione, deve essere definita in ogni unità di traduzione in cui è utilizzata odr e il comportamento del programma è come se c'è esattamente una variabile.
e modificato [basic.def] p2:
Una dichiarazione è una definizione a meno che
...
- dichiara un membro di dati statici al di fuori di una definizione di classe e la variabile è stata definita all'interno della classe con l'identificatore constexpr (questo uso è obsoleto; vedere [depr.static_constexpr]),
...
e aggiungi [depr.static_constexpr] :
Per compatibilità con i precedenti standard internazionali C ++, un membro di dati statici constexpr può essere ridondantemente dichiarato al di fuori della classe senza inizializzatore. Questo utilizzo è obsoleto. [ Esempio:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
- fine esempio]
C ++ 14 e precedenti
In C ++ 03, ci è stato permesso di fornire solo inizializzatori in classe per integrali const o tipi di enumerazione const , in C ++ 11 l'utilizzo di constexpr
questo è stato esteso ai tipi letterali .
In C ++ 11, non è necessario fornire una definizione dell'ambito dello spazio dei nomi per un constexpr
membro statico se non viene utilizzato in modo dispari , lo si può vedere dalla bozza della sezione standard C ++ 11 9.4.2
[class.static.data] che dice ( enfatizzare il mio andare avanti ):
[...] Un membro di dati statici di tipo letterale può essere dichiarato nella definizione della classe con l'identificatore constexpr; in tal caso, la sua dichiarazione deve specificare un inizializzatore parentesi graffa o uguale in cui ogni clausola di inizializzazione che è un'espressione di assegnazione è un'espressione costante. [Nota: in entrambi questi casi, il membro può apparire in espressioni costanti. —End note]
Il membro deve essere comunque definito nell'ambito di uno spazio dei nomi se viene utilizzato odr (3.2) nel programma e la definizione di ambito dello spazio dei nomi non deve contenere un inizializzatore.
Quindi la domanda diventa, qui è baz
usato odr :
std::string str(baz);
e la risposta è sì , e quindi abbiamo bisogno anche di una definizione dell'ambito dello spazio dei nomi.
Quindi, come possiamo determinare se una variabile viene utilizzata odr ? Il testo originale in C ++ 11 nella sezione 3.2
[basic.def.odr] dice:
Un'espressione viene potenzialmente valutata a meno che non sia un operando non valutato (clausola 5) o una sua sottoespressione. Una variabile il cui nome appare come espressione potenzialmente valutata viene utilizzata in modo dispari a meno che
non sia un oggetto che soddisfi i requisiti per apparire in un'espressione costante (5.19) e la conversione da valore in valore (4.1) viene immediatamente applicata .
Quindi baz
produce un'espressione costante ma il conversione da lvalue a rvalue non viene immediatamente applicata poiché non è applicabile a causa di baz
un array. Questo è trattato nella sezione 4.1
[conv.lval] che dice:
Un glvalue (3.10) di un tipo T non funzionale e non array può essere convertito in un valore.53 [...]
Cosa viene applicato nella conversione da array a puntatore .
Questa formulazione di [basic.def.odr] è stata modificata a causa del Rapporto sui difetti 712 poiché alcuni casi non erano coperti da questa formulazione ma queste modifiche non cambiano i risultati per questo caso.