membro constexpr statico dello stesso tipo della class in fase di definizione

Vorrei che una class C avesse un membro constexpr statico di tipo C. Ciò è ansible in C ++ 11?

Tentativo 1:

struct Foo { constexpr Foo() {} static constexpr Foo f = Foo(); }; constexpr Foo Foo::f; 

g ++ 4.7.0 dice: ‘uso non valido di tipo incompleto’ riferito alla chiamata Foo() .

Tentativo 2:

 struct Foo { constexpr Foo() {} static constexpr Foo f; }; constexpr Foo Foo::f = Foo(); 

Ora il problema è la mancanza di un inizializzatore per il membro constexpr f all’interno della definizione della class.

Tentativo 3:

 struct Foo { constexpr Foo() {} static const Foo f; }; constexpr Foo Foo::f = Foo(); 

Ora g ++ si lamenta di una ridichiarazione di Foo::f diversa da constexpr .

Se interpreto correttamente lo standard, non è ansible.

(§9.4.2 / 3) […] Un membro di dati statici di tipo letterale può essere dichiarato nella definizione di class con lo specificatore di constexpr; in tal caso, la sua dichiarazione deve specificare un inizializzatore di coppia o uguale in cui ogni clausola di inizializzazione che è un’espressione di assegnazione è un’espressione costante. […]

Da quanto sopra (insieme al fatto che non esiste un’istruzione separata sui tipi non letterali nelle dichiarazioni dei membri di dati statici), credo che un membro di dati statici che è constexpr deve essere un tipo letterale (come definito in §3.9 / 10), e deve avere la sua definizione inclusa nella dichiarazione . Quest’ultima condizione potrebbe essere soddisfatta utilizzando il seguente codice:

 struct Foo { constexpr Foo() {} static constexpr Foo f {}; }; 

che è simile al Tentativo 1, ma senza la definizione esterna della class.

Tuttavia, poiché Foo è incompleto al momento della dichiarazione / definizione del membro statico, il compilatore non può verificare se si tratta di un tipo letterale (come definito in §3.9 / 10), quindi rifiuta il codice.

Si noti che esiste questo documento post-C ++-11 (N3308) che discute vari problemi dell’attuale definizione di constexpr nello Standard e suggerisce suggerimenti per gli emendamenti. Nello specifico, la sezione “Proposed Wording” suggerisce un emendamento del §3.9 / 10 che implica l’inclusione di tipi incompleti come un tipo di tipo letterale. Se questo emendamento dovesse essere accettato in una versione futura dello standard, il problema sarebbe risolto.

Credo che GCC non sia corretto nel rifiutare il tuo tentativo 3. Non esiste alcuna regola nello standard C ++ 11 (o in uno qualsiasi dei suoi rapporti sui difetti accettati) che dice che una ridichiarazione di una variabile deve essere constexpr se la dichiarazione precedente era. Lo standard più vicino a questa regola è in [dcl.constexpr] (7.1.5) / 1_ :

Se una dichiarazione di una funzione o di un modello di funzione è specificata da constexpr , allora tutte le sue dichiarazioni devono contenere lo specificatore del constexpr .

L’implementazione di constexpr di constexpr accetta il tuo tentativo 3.

Un aggiornamento sulla risposta di Richard Smith , il tentativo 3 ora compila sia su GCC 4.9 che su 5.1, oltre a clang 3.4.

 struct Foo { std::size_t v; constexpr Foo() : v(){} static const Foo f; }; constexpr const Foo Foo::f = Foo(); std::array a; 

Tuttavia, quando Foo è un modello di class, clang 3.4 fallisce, ma GCC 4.9 e 5.1 funzionano ancora bene:

 template < class T > struct Foo { T v; constexpr Foo() : v(){} static const Foo f; }; template < class T > constexpr const Foo Foo::f = Foo(); std::array::fv> a; // gcc ok, clang complains 

Errore clang:

 error: non-type template argument is not a constant expression std::array::fv> a; ^~~~~~~~~~~~~~~~~~~~~