Initialisierung von const member innerhalb der Klassendeklaration in C ++

79

In PHP und C # können die Konstanten so initialisiert werden, wie sie deklariert sind:

class Calendar3
{
    const int value1 = 12;
    const double value2 = 0.001;
}

Ich habe die folgende C ++ - Deklaration eines Funktors, der mit einer anderen Klasse verwendet wird, um zwei mathematische Vektoren zu vergleichen:

struct equal_vec
{
    bool operator() (const Vector3D& a, const Vector3D& b) const
    {
        Vector3D dist = b - a;
        return ( dist.length2() <= tolerance );
    }

    static const float tolerance = 0.001;
};

Dieser Code wurde ohne Probleme mit g ++ kompiliert. Im C ++ 0x-Modus (-std = c ++ 0x) gibt der g ++ - Compiler eine Fehlermeldung aus:

Fehler: 'constexpr' wird für die klasseninterne Initialisierung der 'Toleranz' des statischen Datenelements vom nicht integralen Typ benötigt

Ich weiß, dass ich dieses static constMitglied außerhalb der Klassendefinition definieren und initialisieren kann . Außerdem kann ein nicht statisches konstantes Datenelement in der Initialisierungsliste eines Konstruktors initialisiert werden.

Aber gibt es eine Möglichkeit, eine Konstante innerhalb der Klassendeklaration so zu initialisieren, wie es in PHP oder C # möglich ist?

Aktualisieren

Ich habe das staticSchlüsselwort verwendet, nur weil es möglich war, solche Konstanten innerhalb der Klassendeklaration in g ++ zu initialisieren. Ich brauche nur eine Möglichkeit, eine Konstante in einer Klassendeklaration zu initialisieren, egal ob sie als deklariert ist staticoder nicht.

Ezpresso
quelle
5
I used static keyword just because it was possible to initialize such constants within the class declaration in g++. I just need a way to initialize a constant in a class declaration no matter if it declared as static or not.Das ist der falsche Weg, um zu entscheiden, ob ein Mitglied sein soll staticoder nicht. Lassen Sie niemals die lexikalische Faulheit über die Semantik Ihres Codes entscheiden.
Leichtigkeitsrennen im Orbit
That's the wrong way to decide whether a member should be static or not.Ich stimme nicht zu. Ich denke, das ist für ständige Mitglieder nicht wichtig.
Ezpresso
3
@ Expresso: Überhaupt nicht. Sie können ein nicht statickonstantes Mitglied mit instanzspezifischen Informationen initialisieren . Dass Sie entschieden haben, dass Ihre Konstante eher eine Eigenschaft des Typs als einer bestimmten Instanz ist, ist der Grund dafür, staticund nicht, weil Sie sich eine Eingabeverknüpfung vorgestellt haben.
Leichtigkeitsrennen im Orbit
@lightless: Nun, es ist möglich, aber ich sehe keinen Grund, die Initialisierung derselben instanzspezifischen Konstanten mit unterschiedlichen Werten zu verwenden. Ich habe dafür nicht konstante Klassenfelder verwendet!
Ezpresso
3
Warum, wenn sie sich nach der Objektinstanziierung nie ändern? struct myType { const std::time_t instantiated; myType() : instantiated(std::time(0)) {} };Alles, was kann sein const sollte sein const; staticDies gilt sowohl für Nichtmitglieder als auch für staticNichtmitglieder.
Leichtigkeitsrennen im Orbit

Antworten:

135

In C ++ 11 können Nicht- staticDatenelemente, static constexprDatenelemente und static constDatenelemente vom Integral- oder Aufzählungstyp in der Klassendeklaration initialisiert werden. z.B

struct X {
    int i=5;
    const float f=3.12f;
    static const int j=42;
    static constexpr float g=9.5f;
};

In diesem Fall ist das iMitglied aller Instanzen der Klasse Xinitialisiert wird, um 5durch den Compiler generierten Konstruktor und das fElement wird initialisiert 3.12. Das static constDatenelement jwird auf initialisiert 42und das static constexprDatenelement gwird auf initialisiert 9.5.

Da floatund doublenicht vom Integral- oder Aufzählungstyp sind, müssen solche Elemente entweder constexproder nicht sein static, damit der Initialisierer in der Klassendefinition zulässig ist.

Vor C ++ 11 konnten nur static constDatenelemente vom Integral- oder Aufzählungstyp Initialisierer in der Klassendefinition haben.

Anthony Williams
quelle
Gibt es einen Link zu dem Teil des Standards, in dem dies spezifiziert wurde? Ich habe gerade angefangen, es zu verwenden (obwohl es als C ++ 14 kompiliert wurde) und freue mich, dass es funktioniert, da ich viel Zeit sparen kann, indem der Compiler dies für mich erledigt. Doch bis die Antwort zu lesen, war ich nicht sicher , ob es soll arbeiten werden! Ein offizielles Wort würde mein Vertrauen stärken, haha. Fwiw Ich habe es auch mit Mitgliedern wie arbeiten char const n[3]{'a', 'b', 'c'};.
underscore_d
Ich frage mich warum static const intaber static constexpr float? was bedeutet es float and double are not of integral or enumeration type?
Mrgloom
@ Mrgloom: Ja, Float und Double sind es nicht. Wenn der Datentyp in eine ganze Zahl übersetzt werden kann, handelt es sich um einen ganzzahligen Typ.
ppadhy
45

Das Initialisieren anderer statischer Elementvariablen als const int-Typen ist vor C ++ 11 kein Standard-C ++. Der gcc-Compiler warnt Sie nicht davor (und erzeugt dennoch nützlichen Code), es sei denn, Sie geben die -pedanticOption an. Sie sollten dann einen Fehler erhalten, der ähnlich ist wie:

const.cpp:3:36: error: floating-point literal cannot appear in a constant-expression
const.cpp:3:36: warning: ISO C++ forbids initialization of member constant ‘tolerance’ of non-integral type ‘const float’ [-pedantic]

Der Grund dafür ist, dass der C ++ - Standard nicht spezifiziert, wie Gleitkomma implementiert werden soll, und dem Prozessor überlassen bleibt. Um diese und andere Einschränkungen zu umgehen, constexprwurde eingeführt.

Florian
quelle
14

Ja. Fügen Sie einfach das constexprSchlüsselwort hinzu, wie der Fehler sagt.

StilesCrisis
quelle
1
Vielleicht möchte er C ++ 11 nicht für sein Projekt benötigen?
Marc Mutz - mmutz
6
Das Problem, das er erwähnt, wurde aufgrund der Tatsache eingeführt, dass er versuchte, als C ++ 11 zu kompilieren, dh er möchte C ++ 11 unterstützen :)
Unbekannt1987
1

Wenn Sie es nur in einer Methode benötigen, können Sie es lokal statisch deklarieren:

struct equal_vec
{
    bool operator() (const Vector3D& a, const Vector3D& b) const
    {
        static const float tolerance = 0.001f;
        Vector3D dist = b - a;
        return ( dist.length2() <= tolerance );
    }
};
Peter Wood
quelle
1

Ich bin dabei auf echte Probleme gestoßen, weil ich denselben Code zum Kompilieren mit verschiedenen Versionen von g ++ (dem GNU C ++ - Compiler) benötige. Also musste ich ein Makro verwenden, um zu sehen, welche Version des Compilers verwendet wurde, und dann entsprechend handeln

#if __GNUC__ > 5
 #define GNU_CONST_STATIC_FLOAT_DECLARATION constexpr
#else
 #define GNU_CONST_STATIC_FLOAT_DECLARATION const
#endif

GNU_CONST_STATIC_FLOAT_DECLARATION static double yugeNum=5.0;

Dies verwendet 'const' für alles vor g ++ Version 6.0.0 und dann 'constexpr' für g ++ Version 6.0.0 und höher. Das ist eine Vermutung für die Version, in der die Änderung stattfindet, denn ehrlich gesagt habe ich dies erst in g ++ Version 6.2.1 bemerkt. Um es richtig zu machen, müssen Sie sich möglicherweise die Nebenversion und die Patch-Nummer von g ++ ansehen

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html

Einzelheiten zu den verfügbaren Makros.

Mit gnu könnten Sie auch überall bei der Verwendung von 'const' bleiben und dann mit dem -fpermissiveFlag kompilieren , aber das gibt Warnungen aus und ich möchte, dass meine Sachen sauber kompiliert werden.

Nicht großartig, weil es spezifisch für Gnu-Compiler ist, aber ich vermute, Sie könnten es mit anderen Compilern ähnlich machen.

nilesOien
quelle