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 definieren SOME_VALUE als enum". 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 mit const & 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 mit noref(SOME_VALUE) wohin mit SOME_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 ein static 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.

Schreibe einen Kommentar