Je voudrais avoir une constante statique privée pour une classe (dans ce cas, une fabrique de formes).
J'aimerais avoir quelque chose du genre.
class A {
private:
static const string RECTANGLE = "rectangle";
}
Malheureusement, j'obtiens toutes sortes d'erreurs du compilateur C ++ (g ++), telles que:
ISO C ++ interdit l'initialisation du membre 'RECTANGLE'
initialisation en classe non valide d'un membre de données statiques de type non intégral 'std :: string'
erreur: rendre 'RECTANGLE' statique
Cela m'indique que ce type de conception de membre n'est pas conforme à la norme. Comment avez-vous une constante littérale privée (ou peut-être publique) sans avoir à utiliser une directive #define (je veux éviter la laideur de la globalité des données!)
Toute aide est appréciée.
Réponses:
Vous devez définir votre membre statique en dehors de la définition de classe et y fournir l'initialiseur.
Première
et alors
La syntaxe que vous essayiez à l'origine d'utiliser (initialiseur dans la définition de classe) n'est autorisée qu'avec les types intégral et enum.
À partir de C ++ 17, vous avez une autre option, qui est assez similaire à votre déclaration d'origine: les variables en ligne
Aucune définition supplémentaire n'est nécessaire.
Ou au lieu de
const
vous pouvez le déclarerconstexpr
dans cette variante. Expliciteinline
ne serait plus nécessaire, carconstexpr
impliqueinline
.la source
char const*
a la bonté qu'il est initialisé avant toute initialisation dynamique. Donc, dans le constructeur de n'importe quel objet, vous pouvez compter surRECTANGLE
avoir déjà été initialisé.En C ++ 11, vous pouvez maintenant:
la source
constexpr
impliqueconst
pour var, pas pour taper it points. C'est à direstatic constexpr const char* const
la même chose questatic constexpr const char*
, mais pas la même chose questatic constexpr char*
.Dans les définitions de classe, vous ne pouvez déclarer que des membres statiques. Ils doivent être définis en dehors de la classe. Pour les constantes intégrales au moment de la compilation, la norme fait l'exception que vous pouvez «initialiser» les membres. Ce n'est toujours pas une définition, cependant. Prendre l'adresse ne fonctionnerait pas sans définition, par exemple.
Je voudrais mentionner que je ne vois pas l'avantage d'utiliser std :: string sur const char [] pour les constantes . std :: string est sympa et tout mais nécessite une initialisation dynamique. Donc, si vous écrivez quelque chose comme
au niveau de l'espace de noms, le constructeur de foo sera exécuté juste avant l'exécution du démarrage principal et ce constructeur créera une copie de la constante "hello" dans la mémoire du tas. Sauf si vous avez vraiment besoin que RECTANGLE soit une chaîne std ::, vous pourriez tout aussi bien écrire
Là! Pas d'allocation de tas, pas de copie, pas d'initialisation dynamique.
Santé, art.
la source
Ce ne sont que des informations supplémentaires, mais si vous voulez vraiment la chaîne dans un fichier d'en-tête, essayez quelque chose comme:
Bien que je doute que ce soit recommandé.
la source
En C ++ 17, vous pouvez utiliser des variables en ligne :
Notez que ceci est différent de la réponse d' abyss.7 : Celui-ci définit un
std::string
objet réel , pas unconst char*
la source
inline
créera beaucoup de doublons?C'est la restriction. Par conséquent, dans ce cas, vous devez définir une variable en dehors de la classe. renvoyer une réponse de @AndreyT
la source
Les variables statiques de classe peuvent être déclarées dans l'en-tête mais doivent être définies dans un fichier .cpp. En effet, il ne peut y avoir qu'une seule instance d'une variable statique et le compilateur ne peut pas décider dans quel fichier objet généré le placer, vous devez donc prendre la décision à la place.
Pour conserver la définition d'une valeur statique avec la déclaration en C ++ 11, une structure statique imbriquée peut être utilisée. Dans ce cas, le membre statique est une structure et doit être défini dans un fichier .cpp, mais les valeurs sont dans l'en-tête.
Au lieu d'initialiser des membres individuels, toute la structure statique est initialisée en .cpp:
Les valeurs sont accessibles avec
ou - puisque les membres sont privés et sont destinés à être utilisés uniquement à partir de A - avec
Notez que cette solution souffre toujours du problème de l'ordre d'initialisation des variables statiques. Lorsqu'une valeur statique est utilisée pour initialiser une autre variable statique, la première peut ne pas encore être initialisée.
Dans ce cas, la variable statique têtes des contiendront {""} ou {".h", ".hpp"}, selon l'ordre d'initialisation créé par l'éditeur de liens.
Comme mentionné par @ abyss.7, vous pouvez également utiliser
constexpr
si la valeur de la variable peut être calculée au moment de la compilation. Mais si vous déclarez vos chaînes avecstatic constexpr const char*
et que votre programme utilisestd::string
autrement, il y aura une surcharge car un nouvelstd::string
objet sera créé chaque fois que vous utiliserez une telle constante:la source
La norme actuelle ne permet une telle initialisation que pour les types intégraux à constante statique. Vous devez donc faire comme AndreyT l'a expliqué. Cependant, cela sera disponible dans la prochaine norme via la nouvelle syntaxe d'initialisation des membres .
la source
possible juste faire:
ou
la source
constexpr
mais que vous ne pouvez pas créer une fonction statiqueconst
.static const std::string RECTANGLE() const { static const std::string value("rectangle"); return value; }
Vous pouvez soit opter pour la
const char*
solution mentionnée ci-dessus, mais si vous avez besoin de chaîne tout le temps, vous aurez beaucoup de frais généraux.D'un autre côté, la chaîne statique a besoin d'une initialisation dynamique, donc si vous souhaitez utiliser sa valeur lors de l'initialisation d'une autre variable globale / statique, vous pourriez rencontrer le problème de l'ordre d'initialisation. Pour éviter cela, le moins cher est d'accéder à l'objet chaîne statique via un getter, qui vérifie si votre objet est initialisé ou non.
N'oubliez pas d'utiliser uniquement
A::getS()
. Parce que tout threading ne peut être démarré que parmain()
, etA_s_initialized
est initialisé auparavantmain()
, vous n'avez pas besoin de verrous même dans un environnement multithread.A_s_initialized
est 0 par défaut (avant l'initialisation dynamique), donc si vous utilisezgetS()
avant que s soit initialisé, vous appelez la fonction init en toute sécurité.Btw, dans la réponse ci-dessus: " statique const std :: string RECTANGLE () const ", les fonctions statiques ne peuvent pas être
const
parce qu'elles ne peuvent pas changer l'état si un objet de toute façon (il n'y a pas ce pointeur).la source
Avance rapide vers 2018 et C ++ 17.
static_assert 'fonctionne' au moment de la compilation uniquement
};
Ci-dessus est un citoyen C ++ standard approprié et légal. Il peut être facilement impliqué dans tous les algorithmes std ::, conteneurs, utilitaires et autres. Par exemple:
Profitez du C ++ standard
la source
std::string_view
pour les constantes uniquement si vous utilisez desstring_view
paramètres dans toutes vos fonctions. Si l'une de vos fonctions utilise unconst std::string&
paramètre, une copie d'une chaîne sera créée lorsque vous passerez unestring_view
constante dans ce paramètre. Si vos constantes sont de type,std::string
les copies ne seront créées ni pour lesconst std::string&
paramètres ni pour lesstd::string_view
paramètres.inline
arrivée des variables en C ++ 17 avec leur sémantique ODR. Mais string_view est aussi C ++ 17, donc fait justeconstexpr auto some_str = "compile time"sv;
le travail (et en fait, ce n'est pas une variable, c'estconstexpr
, doncinline
est implicite; si vous avez une variable - c'est-à-dire nonconstexpr
- alors leinline auto some_str = "compile time"sv;
fera, bien que naturellement une étendue d'espace de noms variable, qui est essentiellement une variable globale, serait rarement une bonne idée).