Warum kann ich kein nicht konstantes static
Mitglied oder static
Array in einer Klasse initialisieren ?
class A
{
static const int a = 3;
static int b = 3;
static const int c[2] = { 1, 2 };
static int d[2] = { 1, 2 };
};
int main()
{
A a;
return 0;
}
Der Compiler gibt folgende Fehler aus:
g++ main.cpp
main.cpp:4:17: error: ISO C++ forbids in-class initialization of non-const static member ‘b’
main.cpp:5:26: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:5:33: error: invalid in-class initialization of static data member of non-integral type ‘const int [2]’
main.cpp:6:20: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:6:27: error: invalid in-class initialization of static data member of non-integral type ‘int [2]’
Ich habe zwei Fragen:
- Warum kann ich
static
Datenelemente in der Klasse nicht initialisieren ? - Warum kann ich
static
Arrays in der Klasse nicht initialisieren , auch nicht dasconst
Array?
Antworten:
Warum kann ich
static
Datenelemente in der Klasse nicht initialisieren ?Mit dem C ++ - Standard können nur statische Konstantenintegral- oder Aufzählungstypen innerhalb der Klasse initialisiert werden. Dies ist der Grund, warum
a
die Initialisierung zulässig ist, während andere dies nicht tun.Referenz:
C ++ 03 9.4.2 Statische Datenelemente
§4
Was sind integrale Typen?
C ++ 03 3.9.1 Grundtypen
§7
Fußnote:
Problemumgehung:
Mit dem Enum-Trick können Sie ein Array in Ihrer Klassendefinition initialisieren.
Warum erlaubt der Standard dies nicht?
Bjarne erklärt dies hier treffend :
Warum sind nur
static const
integrale Typen und Aufzählungen in der Klasseninitialisierung zulässig?Die Antwort ist in Bjarnes Zitat verborgen. Lesen Sie es genau durch:
"C ++ erfordert, dass jedes Objekt eine eindeutige Definition hat. Diese Regel würde verletzt, wenn C ++ die Definition von Entitäten in der Klasse zulässt, die als Objekte im Speicher gespeichert werden müssen."
Beachten Sie, dass nur
static const
Ganzzahlen als Konstanten für die Kompilierungszeit behandelt werden können. Der Compiler weiß, dass sich der ganzzahlige Wert zu keiner Zeit ändert und daher seine eigene Magie anwenden und Optimierungen anwenden kann. Der Compiler fügt solche Klassenmitglieder einfach ein, dh sie werden nicht mehr im Speicher gespeichert, da die Notwendigkeit, im Speicher gespeichert zu werden, beseitigt wird gibt es solchen Variablen die Ausnahme von der von Bjarne erwähnten Regel.Hierbei ist zu beachten, dass selbst wenn
static const
Integralwerte eine In-Class-Initialisierung aufweisen können, die Adressierung solcher Variablen nicht zulässig ist. Man kann die Adresse eines statischen Mitglieds nehmen, wenn (und nur wenn) es eine Definition außerhalb der Klasse hat. Dies bestätigt die obige Argumentation weiter.Aufzählungen sind zulässig, da Werte eines Aufzählungstyps verwendet werden können, wenn Ints erwartet werden. siehe Zitat oben
Wie ändert sich dies in C ++ 11?
C ++ 11 lockert die Einschränkung bis zu einem gewissen Grad.
C ++ 11 9.4.2 Statische Datenelemente
§3
Auch C ++ 11 wird ermöglichen (§12.6.2.8) ein nicht-statisches Datenelement initialisiert werden , wo sie (in dieser Klasse) deklariert wird. Dies bedeutet eine sehr einfache Benutzersemantik.
Beachten Sie, dass diese Funktionen in der neuesten Version 4.7 noch nicht implementiert wurden. Daher werden möglicherweise immer noch Kompilierungsfehler angezeigt.
quelle
&member
zurückkehren?static const char*
Mitglieder nicht möglich ist .Dies scheint ein Relikt aus den alten Zeiten einfacher Linker zu sein. Sie können statische Variablen in statischen Methoden als Problemumgehung verwenden:
und
und
bauen:
Lauf:
Die Tatsache, dass dies funktioniert (konsistent, auch wenn die Klassendefinition in verschiedenen Kompilierungseinheiten enthalten ist), zeigt, dass der heutige Linker (gcc 4.9.2) tatsächlich intelligent genug ist.
Witzig: Druckt
0123
auf Arm und3210
x86.quelle
Ich denke, es soll Sie daran hindern, Erklärungen und Definitionen zu mischen. (Denken Sie an die Probleme, die auftreten können, wenn Sie die Datei an mehreren Stellen einfügen.)
quelle
Es ist, weil es nur eine Definition von geben kann
A::a
allen Übersetzungseinheiten verwendet wird.Wenn Sie
static int a = 3;
in einer Klasse in einem Header auftreten, der in allen Übersetzungseinheiten enthalten ist, erhalten Sie mehrere Definitionen. Daher wird eine nicht externe Definition einer Statik zwangsweise zu einem Compilerfehler gemacht.Verwenden
static inline
oderstatic const
Abhilfe schaffen.static inline
Konkretisiert das Symbol nur, wenn es in der Übersetzungseinheit verwendet wird, und stellt sicher, dass der Linker nur eine Kopie auswählt und belässt, wenn sie in mehreren Übersetzungseinheiten definiert ist, da sie sich in einer Comdat-Gruppe befindet.const
Im Dateibereich gibt der Compiler niemals ein Symbol aus, da es im Code immer sofort ersetzt wird, es sei dennextern
es nicht verwendet wird, was in einer Klasse nicht zulässig ist.Eine zu beachtende Sache
static inline int b;
ist, dass sie als Definition behandelt wird, währendstatic const int b
oderstatic const A b;
immer noch als Deklaration behandelt wird und außerhalb der Zeile definiert werden muss, wenn Sie sie nicht innerhalb der Klasse definieren. Interessanterweisestatic constexpr A b;
wird es als Definition behandelt, währendstatic constexpr int b;
es sich um einen Fehler handelt und einen Initialisierer haben muss (dies liegt daran, dass sie jetzt zu Definitionen werden und wie jede const / constexpr-Definition im Dateibereich einen Initialisierer benötigen, den ein int nur über einen Klassentyp verfügt funktioniert, weil es implizit ist,= A()
wenn es sich um eine Definition handelt - clang erlaubt dies, aber gcc erfordert, dass Sie explizit initialisieren, oder es ist ein Fehler. Dies ist kein Problem mit Inline).static const A b = A();
ist nicht erlaubt und mussconstexpr
oder seininline
um einen Initialisierer für ein statisches Objekt mit Klassentyp zuzulassen, dh ein statisches Element vom Klassentyp mehr als eine Deklaration zu machen. Ja, in bestimmten SituationenA a;
ist dies nicht dasselbe wie explizites InitialisierenA a = A();
(Ersteres kann eine Deklaration sein, aber wenn nur eine Deklaration für diesen Typ zulässig ist, ist Letzteres ein Fehler. Letzteres kann nur für eine Definition verwendet werden.constexpr
Macht es zu einer Definition ). Wenn Sieconstexpr
einen Standardkonstruktor verwenden und angeben, muss dies der Konstruktor seinconstexpr
Ein statisches Element ist eine vollständige Deklaration des Dateibereichs
extern int A::a;
(die nur in der Klasse erstellt werden kann und Definitionen außerhalb der Zeile müssen sich auf ein statisches Element in einer Klasse beziehen und müssen Definitionen sein und dürfen kein externes Element enthalten), während ein nicht statisches Element Teil von ist die vollständige Typdefinition einer Klasse und haben die gleichen Regeln wie Dateibereichsdeklarationen ohneextern
. Sie sind implizit Definitionen. Soint i[]; int i[5];
ist eine Neudefinition der Erwägung , dassstatic int i[]; int A::i[5];
nicht aber im Gegensatz zu 2 Externen, wird der Compiler noch ein Duplikat Mitglied erkennen , wenn Sie tunstatic int i[]; static int i[5];
in der Klasse.quelle
statische Variablen sind klassenspezifisch. Konstruktoren initialisieren Attribute ESPECIALY für eine Instanz.
quelle