Stringa statica costante C ++ (membro della class)

Mi piacerebbe avere una costante statica privata per una class (in questo caso una fabbrica di forms). Mi piacerebbe avere qualcosa del genere.

class A { private: static const string RECTANGLE = "rectangle"; } 

Sfortunatamente ricevo ogni sorta di errore dal compilatore C ++ (g ++), come ad esempio:

ISO C ++ vieta l’inizializzazione del membro ‘RECTANGLE’

inizializzazione non valida della class di dati statici membro di tipo non integrale ‘std :: string’

errore: fare “RETTANGOLO” statico

Questo mi dice che questo tipo di design dei membri non è conforms allo standard. Come si dispone di una costante letterale privata (o forse pubblica) senza dover utilizzare una direttiva #define (voglio evitare la bruttura della globalità dei dati!)

Qualsiasi aiuto è apprezzato. Grazie.

Devi definire il tuo membro statico al di fuori della definizione della class e fornire lì l’inizializzatore.

Primo

 // In a header file (if it is in a header file in your case) class A { private: static const string RECTANGLE; }; 

e poi

 // In one of the implementation files const string A::RECTANGLE = "rectangle"; 

La syntax che si stava originariamente tentando di utilizzare (inizializzatore all’interno della definizione della class) è consentita solo con tipi interi ed enumerati.

In C ++ 11 puoi fare ora:

 class A { private: static constexpr const char* STRING = "some useful string constant"; }; 

Le definizioni di classi interne possono solo dichiarare membri statici. Devono essere definiti al di fuori della class. Per le costanti integrali in fase di compilazione, lo standard rende l’eccezione che è ansible “inizializzare” i membri. Non è ancora una definizione, però. Prendendo l’indirizzo non funzionerebbe senza definizione, per esempio.

Vorrei dire che non vedo il vantaggio dell’uso di std :: string su const char [] per le costanti . std :: string è bello e tutto, ma richiede l’inizializzazione dynamic. Quindi, se scrivi qualcosa del genere

 const std::string foo = "hello"; 

nello spazio dei nomi il costruttore di foo verrà eseguito subito prima dell’esecuzione degli avvii principali e questo costruttore creerà una copia della costante “ciao” nella memoria heap. A meno che tu non abbia davvero bisogno che RECTANGLE sia una std :: string, potresti scrivere altrettanto bene

 // class definition with incomplete static member could be in a header file class A { static const char RECTANGLE[]; }; // this needs to be placed in a single translation unit only const char A::RECTANGLE[] = "rectangle"; 

Là! Nessuna allocazione dell’heap, nessuna copia, nessuna inizializzazione dynamic.

Saluti, s.

Questa è solo un’informazione in più, ma se vuoi davvero la stringa in un file di intestazione, prova qualcosa del tipo:

 class foo { public: static const std::string& RECTANGLE(void) { static const std::string str = "rectangle"; return str; } }; 

Anche se dubito che sia raccomandato.

Per utilizzare la syntax di inizializzazione in class, la costante deve essere una costante statica di tipo integrale o di enumerazione inizializzata da un’espressione costante.

Questa è la restrizione. Quindi, in questo caso è necessario definire una variabile al di fuori della class. refer answwer da @AndreyT

In C ++ 17 puoi usare variabili inline :

 class A { private: static inline const std::string my_string = "some useful string constant"; }; 

Si noti che questo è diverso dalla risposta di abyss.7 : questo definisce un object std::string effettivo, non un const char*

Lo standard corrente consente solo tale inizializzazione per tipi interi costanti statici. Quindi devi fare come ha spiegato AndreyT. Tuttavia, questo sarà disponibile nello standard successivo attraverso la syntax di inizializzazione del nuovo membro .

ansible basta fare:

 static const std::string RECTANGLE() const { return "rectangle"; } 

o

 #define RECTANGLE "rectangle" 

Puoi scegliere la soluzione const char* sopra menzionata, ma se hai bisogno di stringhe per tutto il tempo, avrai un sacco di spese generali.
D’altra parte, la stringa statica ha bisogno dell’inizializzazione dynamic, quindi se si desidera utilizzare il suo valore durante l’inizializzazione di un’altra variabile globale / statica, si potrebbe riscontrare il problema dell’ordine di inizializzazione. Per evitare ciò, la cosa più economica è accedere all’object stringa statico attraverso un getter, che controlla se il tuo object è inizializzato o meno.

 //in a header class A{ static string s; public: static string getS(); }; //in implementation string A::s; namespace{ bool init_A_s(){ A::s = string("foo"); return true; } bool A_s_initialized = init_A_s(); } string A::getS(){ if (!A_s_initialized) A_s_initialized = init_A_s(); return s; } 

Ricorda di usare solo A::getS() . Poiché qualsiasi threading può essere avviato solo da main() e A_s_initialized viene inizializzato prima di main() , non è necessario il lock anche in un ambiente con multithreading. A_s_initialized è 0 di default (prima dell’inizializzazione dynamic), quindi se si usa getS() prima che s sia inizializzato, si chiama in modo sicuro la funzione init.

Btw, nella risposta sopra: ” statico const std :: string RECTANGLE () const “, le funzioni statiche non possono essere const perché non possono cambiare lo stato se qualsiasi object comunque (non esiste questo puntatore).

Le variabili statiche di class possono essere dichiarate nell’intestazione, ma devono essere definite in un file .cpp. Questo perché ci può essere solo una istanza di una variabile statica e il compilatore non può decidere in quale file object generato metterlo in modo che tu debba prendere la decisione, invece.

Per mantenere la definizione di un valore statico con la dichiarazione in C ++ 11, è ansible utilizzare una struttura statica nidificata. In questo caso il membro statico è una struttura e deve essere definito in un file .cpp, ma i valori sono nell’intestazione.

 class A { private: static struct _Shapes { const std::string RECTANGLE {"rectangle"}; const std::string CIRCLE {"circle"}; } shape; }; 

Invece di inizializzare i singoli membri, l’intera struttura statica viene inizializzata in .cpp:

 A::_Shapes A::shape; 

I valori sono accessibili con

 A::shape.RECTANGLE; 

oppure – poiché i membri sono privati ​​e devono essere utilizzati solo da A – con

 shape.RECTANGLE; 

Si noti che questa soluzione soffre ancora del problema dell’ordine di inizializzazione delle variabili statiche. Quando un valore statico viene utilizzato per inizializzare un’altra variabile statica, il primo potrebbe non essere ancora inizializzato.

 // file.h class File { public: static struct _Extensions { const std::string h{ ".h" }; const std::string hpp{ ".hpp" }; const std::string c{ ".c" }; const std::string cpp{ ".cpp" }; } extension; }; // file.cpp File::_Extensions File::extension; // module.cpp static std::set headers{ File::extension.h, File::extension.hpp }; 

In questo caso le intestazioni delle variabili statiche conterranno {“”} o {“.h”, “.hpp”}, a seconda dell’ordine di inizializzazione creato dal linker.

Come menzionato da @ abyss.7, è ansible utilizzare anche constexpr se il valore della variabile può essere calcolato in fase di compilazione. Ma se dichiari le tue stringhe con static constexpr const char* e il tuo programma usa std::string altrimenti ci sarà un sovraccarico perché un nuovo object std::string verrà creato ogni volta che usi una costante di questo tipo:

 class A { public: static constexpr const char* STRING = "some value"; }; void foo(const std::string& bar); int main() { foo(A::STRING); // a new std::string is constructed and destroyed. } 

Avanzamento rapido fino al 2018 e C ++ 17.

 using namespace std::literals; namespace STANDARD { constexpr inline auto compiletime_static_string_view_constant() { // make and return string view literal // will stay the same for the whole application lifetime // will exhibit standard and expected interface // will be usable at both // runtime and compile time // by value semantics implemented for you auto made_once_when_needed_ = "compile time"sv; return made_once_when_needed_ ; } }; 

Sopra è un cittadino C ++ standard corretto e legale. Può essere facilmente coinvolto in qualsiasi e tutti gli std :: algoritmi, contenitori, utilità e un tale. Per esempio:

 // test the resilience auto return_by_val = []() { auto return_by_val = []() { auto return_by_val = []() { auto return_by_val = []() { return STANDARD::compiletime_static_string_view_constant(); }; return return_by_val(); }; return return_by_val(); }; return return_by_val(); }; // actually a run time _ASSERTE(return_by_val() == "compile time"); // compile time static_assert( STANDARD::compiletime_static_string_view_constant() == "compile time" ); 

Goditi il ​​C ++ standard