enum vs constexpr für die tatsächliche statische Konstanten innerhalb von Klassen
Lassen Sie mich beginnen mit der Feststellung, meine Absicht. In den alten (C++) Tage, wir hätten einen code wie:
class C
{
public:
enum {SOME_VALUE=27};
};
Dann könnten wir SOME_VALUE
überall in unserem code einen compile-Zeit-Konstante und überall dort, wo der compiler würde sehen C::SOME_VALUE
, es wäre nur legen Sie die wörtliche 27.
Jetzt Tage, es scheint akzeptabler zu ändern, dass der code so etwas wie:
class C
{
public:
static constexpr int SOME_VALUE=27;
};
Das sieht viel sauberer aus, gibt SOME_VALUE
eine gut definierte Art zu sein scheint: der am günstigsten gelegene Ansatz von C++11. Die (vorhersehbare zumindest für mich) problem ist, dass dies verursacht auch Szenarien, in denen SOME_VALUE
muss gemacht werden externe. Das heißt, in einigen cpp-Datei, die irgendwo, wir müssen hinzufügen:
constexpr int C::SOME_VALUE; //Now C::SOME_VALUE has external linkage
Den Fällen, die Ursache dafür zu sein scheinen, wenn const-Referenzen zu SOME_VALUE
verwendet werden, das passiert sehr oft in der C++ Standard Library code (Siehe das Beispiel am Ende dieser Frage). Ich bin mit gcc 4.7.2, wie mein compiler durch die Art und Weise.
Durch dieses dilemma bin ich gezwungen, um wieder zurück zu definieren SOME_VALUE
als enum (D. H., old school), um zu vermeiden, dass Sie eine definition an, die eine cpp-Datei, die für einige, aber nicht alle meine static constexpr-member-Variablen. Gibt es nicht ein Weg, zu sagen, der compiler, dass constexpr int SOME_VALUE=27
bedeutet, dass SOME_VALUE
behandelt werden sollte nur als eine compile-Zeit-Konstante und niemals ein Objekt mit externer Bindung? Wenn Sie sehen, eine const-Referenz, die Sie mit ihm, erstellen Sie eine temporäre. Wenn Sie sehen, Ihr Adresse genommen, erzeugen einen compile-Zeit-Fehler, wenn das, was gebraucht wird, weil es einen compile-Zeit-Konstanten, und nichts mehr.
Hier einige scheinbar harmloses Beispiel-code, der bewirkt, dass wir brauchen, um hinzuzufügen, die definition für SOME_VALUE
in einer cpp-Datei (erneut getestet mit gcc 4.7.2):
#include <vector>
class C
{
public:
static constexpr int SOME_VALUE=5;
};
int main()
{
std::vector<int> iv;
iv.push_back(C::SOME_VALUE); //Will cause an undefined reference error
//at link time, because the compiler isn't smart
//enough to treat C::SOME_VALUE as the literal 5
//even though it's obvious at compile time
}
Fügen Sie folgende Zeile in den code-Datei Umfang wird den Fehler beheben:
constexpr int C::SOME_VALUE;
- Ich bin wirklich verwirrt ", wenn die Adresse von
SOME_VALUE
genommen, ... bin ich gezwungen, um wieder zurück zu definierenSOME_VALUE
alsenum
". Enumeratoren sind prvalues, können Sie nicht nehmen Sie Ihre Adressen entweder. - Ich habe geändert, die Frage nur eine const-Referenzen auf den Wert, so dass unter einer Adresse ist nicht "erforderlich" (d.h., einem temporären erstellt werden können).
- Sie dürfen
static constexpr int SOME_VALUE() { return 5; }
... - BTW, man kann jetzt geben Enumeratoren einen gut definierten Typ:
enum : int { SOME_VALUE = 5 };
- Aber, Enumeratoren beschränken uns auf Integrale Werte - ein weiterer Nachteil der Verwendung von Ihnen.
- Ich glaube nicht, dass
static constexpr int&& SOME_VALUE=27;
ist erlaubt? Wahrscheinlich nicht... "So ein Objekt haben soll-literal type" - Ich denke, diese ganze Frage läuft darauf hinaus, "Warum
constexpr
erstellen Sie ein lvalue?" - Oder vielleicht, warum
constexpr
erstellen Sie ein lvalue, wenn es die Adresse ist nie ergriffen (oder nicht ergriffen werden müssen, wie es der Fall mitconst &
und seine Fähigkeit, Provisorien). - Ähnliche Fragen und Antworten wie [1], [2], [3], die ich gesammelt haben, hier eine Reihe von Problemumgehungen. Das prägnanteste ist die Verwendung
+SOME_VALUE
um ein temporäres. - ist ein interessanter workaround, aber wieder nur für numerische Typen, und diejenigen, für die ein unärer plus-operator definiert ist. Natürlich Enumerationen erlauben nur Integrale Typen als gut.
- Sie definieren Ihre eigene Identität-Funktion, die zwingen würde, einen Wert einer temporären:
template<typename T> constexpr T noref( T t ) { return t; }
. Dies könnte angewendet werden, indem Sie mitnoref(SOME_VALUE)
wohin mitSOME_VALUE
endet, wobei eine Referenz. (Dies würde nur Arbeit für die wörtliche beweglichen oder kopierbar Typen, aber das ist allgemeiner als die+
die arbeiten für numerische Typen, die wörtliche kopierbar-Typen.) - Das zugrunde liegende problem ist, dass die übergabe per Referenz (potentiell) benötigt eine Adresse. Es gibt keine Adresse für etwas, das nicht definiert. So erstellen Sie ein Objekt schließlich an eine Adresse. Das ist, warum viele Lösungen hier mit Provisorien. Ein weiterer Weg, um eine zu erstellen ist die Verwendung einer wrapper mit einem
constexpr
conversion operator. Ich denke, es sollte in Ordnung, wenn es keine Vorlage geben Abzug beteiligt. - Alle diese workarounds zeigen mir, dass mit einem
enum
für die integralen Konstanten ist besser als einstatic constexpr
. Es gibt weniger überraschungen (zumindest sowieso). Bonus für die Arbeit mit älteren C++ und C, zu. - Wenn Sie erwägen, +SOME_VALUE, können Sie auch verwenden, SOME_VALUE(), und definieren Sie es als nur
static constexpr int SOME_VALUE() { return 5; }
. Diese ist ebenso allgemein wie noref Vorlage, und braucht weniger boilerplate. Sie bekommen bonus von SOME_VALUE mit wesentlich anderen Typ als SOME_VALUE(), so dass Sie nie vergessen hinzufügen()
. - Für das Protokoll, Clang kompiliert auch der OP-code, ohne die out-of-class-definition.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Für die Aufzeichnung, die
static constexpr
version funktionieren wird, wie Sie erwartet hatte, das in C++17. Von N4618 Anhang D. 1 [depr.static_constexpr]:Den jeweiligen standard-text, der dies ermöglicht, ist N4618 9.2.3 [class.statisch.Daten]/3:
Diese kommt mit den gleichen Maschinen eingeführt, die nicht-
constexpr
version von der gleichen Sache, inline-statischen Daten-member.Haben Sie drei Möglichkeiten:
Wenn Ihre Klasse mit einer Vorlage, dann setzen Sie die definition der statischen member im header selbst. Compiler ist erforderlich, um zu identifizieren es als eine definition, die nur über mehrere übersetzungseinheiten (siehe [basic.def.OS -]/5)
Wenn Ihre Klasse nicht-Vorlage können Sie leicht steckte es in source-Datei
Alternativ erklären constexpr statische member-Funktion getSomeValue():
Dann würde ich mit der enum-Klasse:
http://en.cppreference.com/w/cpp/language/enum
http://www.stroustrup.com/C++11FAQ.html#enum
Vom ersten link:
enum
ein Typ wie erwähnt im Vorbeigehen durch die OP aber nicht Ihre primäre Frage, die war ob es möglich ist, vermeiden externe Bindung bei der Verwendung vonstatic const[expr]
' stattenum
.Heute der bevorzugte Weg ist:
enum
ein Typ wie erwähnt im Vorbeigehen durch die OP aber nicht Ihre primäre Frage, die war ob es möglich ist, vermeiden externe Bindung bei der Verwendung vonstatic const[expr]
' stattenum
.Aus der C++ - standard N3797 S3.5/2-3
Meine Lesung ist, dass in den folgenden code:
Alle 4 Instanzen
SOME_VALUE
haben interne linkage. Sollten Sie den link mit einem Verweis aufSOME_VALUE
in der gleichen übersetzungseinheit und nicht sichtbar anderswo.Offensichtlich ist die erste ist eine Erklärung und keine definition. Es braucht eine definition, die innerhalb derselben übersetzungseinheit. Wenn GCC sagt so und MSVC nicht, dann MSVC ist falsch.
Für die Zwecke der Ersatz einer enum Nummer 2 sollte gut funktionieren. Es hat immer noch interne Verlinkung, ohne die
static
Stichwort.[Editiert Antwort zu Kommentar]
SOME_VALUE
dass nicht eine definition an, einen Verweis gegen die Online-Streitbeilegung, die nicht ist eine Verknüpfung Problem. Kann man sehr gut mit Namen, die keine Verknüpfung (wie automatische Variablen) oder die interne Verlinkung (wie statische Globale Variablen) als Argumente fürpush_back
zum Beispiel.können Sie dies tun,
Ist dies auch nicht C++11, nur C++98
test.cpp:(.text+0x1a): undefined reference to
C::SOME_VALUE", während die Verknüpfung, wie OP. Scheint der gcc-Fehler.static const int SOME_VALUE;
und externen wäre esconst int C::SOME_VALUE = 5;
aber das ist nicht viel von einer Verbesserung.