Ich habe festgestellt, dass C ++ Folgendes nicht kompiliert:
class No_Good {
static double const d = 1.0;
};
Es wird jedoch gerne eine Variation zulassen, bei der das Double in einen int, unsigned oder einen beliebigen integralen Typ geändert wird:
class Happy_Times {
static unsigned const u = 1;
};
Meine Lösung bestand darin, es zu ändern, um zu lesen:
class Now_Good {
static double d() { return 1.0; }
};
und denke, dass der Compiler klug genug sein wird, um bei Bedarf zu inline ... aber es hat mich neugierig gemacht.
Warum erlauben mir die C ++ - Designer, einen int oder unsigned, aber kein double statisch zu konstituieren?
Bearbeiten: Ich verwende Visual Studio 7.1 (.net 2003) unter Windows XP.
Edit2:
Die Frage wurde beantwortet, aber zum Abschluss der Fehler, den ich sah:
error C2864: 'd' : only const static integral data members can be initialized inside a class or struct
Antworten:
Das Problem ist, dass der Compiler bei einer Ganzzahl normalerweise nie eine Speicheradresse für die Konstante erstellen muss. Es existiert zur Laufzeit nicht und jede Verwendung wird in den umgebenden Code eingefügt. Es kann weiterhin entscheiden, ihm einen Speicherort zuzuweisen - falls seine Adresse jemals verwendet wird (oder wenn sie als konstante Referenz an eine Funktion übergeben wird), muss dies der Fall sein. Um ihm eine Adresse zu geben, muss sie in einer Übersetzungseinheit definiert werden. In diesem Fall müssen Sie die Deklaration von der Definition trennen, da sie sonst in mehreren Übersetzungseinheiten definiert wird.
Bei Verwendung von g ++ ohne Optimierung (
-O0
) werden automatisch konstante Ganzzahlvariablen, jedoch keine konstanten Doppelwerte eingefügt. Bei höheren Optimierungsstufen (z. B.-O1
) werden konstante Doppelwerte eingefügt. Daher wird der folgende Code kompiliert,-O1
aber NICHT um-O0
:// File a.h class X { public: static const double d = 1.0; }; void foo(void); // File a.cc #include <stdio.h> #include "a.h" int main(void) { foo(); printf("%g\n", X::d); return 0; } // File b.cc #include <stdio.h> #include "a.h" void foo(void) { printf("foo: %g\n", X::d); }
Befehlszeile:
Für maximale Portabilität sollten Sie Ihre Konstanten in Header-Dateien deklarieren und einmal in einer Quelldatei definieren. Ohne Optimierung wird die Leistung nicht beeinträchtigt, da Sie sowieso nicht optimieren. Wenn jedoch Optimierungen aktiviert sind, kann dies die Leistung beeinträchtigen, da der Compiler diese Konstanten nicht mehr in andere Quelldateien einbinden kann, es sei denn, Sie aktivieren die Optimierung des gesamten Programms. .
quelle
the compiler usually doesn't have to ever create a memory address for the constant
: Würde die Verwendung der Adresse einer Konstanten und das Speichern in einer Variablen den Compiler als Gedankenexperiment zwingen, Speicher für die Konstante zu erstellen?Ich sehe keinen technischen Grund warum
struct type { static const double value = 3.14; };
ist verboten. Jeder Anlass, bei dem Sie feststellen, wo es funktioniert, ist auf nicht portierbare implementierungsdefinierte Funktionen zurückzuführen. Sie scheinen auch nur von begrenztem Nutzen zu sein. Für in Klassendefinitionen initialisierte Integralkonstanten können Sie sie verwenden und als Nicht-Typ-Argumente an Vorlagen übergeben und als Größe der Array-Dimensionen verwenden. Für Gleitkommakonstanten ist dies jedoch nicht möglich. Das Zulassen von Gleitkomma-Vorlagenparametern würde eigene Regeln mit sich bringen, die die Mühe nicht wirklich wert sind.
Die nächste C ++ - Version erlaubt dies jedoch mit
constexpr
:struct type { static constexpr double value = 3.14; static constexpr double value_as_function() { return 3.14; } };
Und wird
type::value
einen konstanten Ausdruck machen. In der Zwischenzeit ist es am besten, dem Muster zu folgen, das auch verwendet wird vonstd::numeric_limits
:struct type { static double value() { return 3.14; } };
Es wird kein konstanter Ausdruck zurückgegeben (Wert ist zur Kompilierungszeit nicht bekannt), aber das ist nur theoretisch wichtig, da der Wert in der Praxis sowieso eingefügt wird. Siehe den constexpr- Vorschlag. Es beinhaltet
quelle
1+1
es sich2
bei jeder Implementierung immer um eine Implementierung handelt, können ähnlich einfache Berechnungen mit Gleitkomma-Mathematik auf verschiedenen Gleitkommamodellen unterschiedliche Ergebnisse liefern (wohlgemerkt, ich mag diese Probleme nicht, aber ich weiß, dass sie existieren).static float
s auszuschließen , was für mich sinnvoll ist.Es gibt nicht wirklich eine Begründung, aber hier ist, was Stroustrup dazu in "The C ++ Programming Language Third Edition" zu sagen hat:
Und in Anhang C (Technische Daten) in Abschnitt C.5 (Konstante Ausdrücke) hat Stroustrup Folgendes über "Konstante Ausdrücke" zu sagen:
Beachten Sie, dass er Gleitkomma so gut wie weglässt, weil er in 'konstanten Ausdrücken' spielen kann. Ich vermute, dass Gleitkomma von diesen Arten von konstanten Ausdrücken weggelassen wurde, nur weil sie nicht "einfach" genug sind.
quelle
Ich weiß nicht, warum es ein Double anders als ein Int behandeln würde. Ich dachte, ich hätte dieses Formular schon einmal benutzt. Hier ist eine alternative Problemumgehung:
class Now_Better { static double const d; };
Und in Ihrer CPP-Datei:
double const Now_Better::d = 1.0;
quelle
Hier ist mein Verständnis, das auf Stroustrups Aussage zur Definition in der Klasse basiert
http://www.stroustrup.com/bs_faq2.html#in-class
Im Grunde ist dies nicht zulässig, da C ++ dies nicht zulässt. Um die Linker-Regeln zu vereinfachen, erfordert C ++, dass jedes Objekt eine eindeutige Definition hat.
Das statische Element hat nur eine Instanz im Klassenbereich, nicht wie reguläre statische Variablen, die in C häufig verwendet werden und nur eine Instanz innerhalb einer Übersetzungseinheit haben.
Wenn statisches Element in der Klasse definiert ist und die Klassendefinition in vielen Übersetzungseinheiten enthalten ist, muss der Linker mehr Arbeit leisten, um zu entscheiden, welches statische Element als einziges in allen zugehörigen Übersetzungseinheiten verwendet werden soll.
Bei regulären statischen Variablen können sie jedoch nur innerhalb einer Übersetzungseinheit verwendet werden. Selbst wenn unterschiedliche statische Variablen in unterschiedlichen Übersetzungseinheiten mit demselben Namen verwendet werden, wirken sie sich nicht gegenseitig aus. Linker kann einfache Arbeit leisten, um reguläre statische Variablen innerhalb einer Übersetzungseinheit zu verknüpfen.
Um die Komplikationen zu verringern und die Basisfunktion bereitzustellen, bietet C ++ die einzige klasseninterne Definition für eine statische Konstante vom Integral- oder Aufzählungstyp.
quelle