Inicjowanie elementu członkowskiego const w deklaracji klasy w C ++

80

W PHP i C # stałe można zainicjować tak, jak są zadeklarowane:

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

Mam następującą deklarację C ++ funktora, który jest używany z inną klasą do porównania dwóch wektorów matematycznych:

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;
};

Ten kod skompilowany bez problemów z g ++. Teraz w trybie C ++ 0x (-std = c ++ 0x) kompilator g ++ wyświetla komunikat o błędzie:

błąd: „constexpr” jest potrzebne do inicjalizacji w klasie statycznego elementu członkowskiego danych „tolerancja” typu niecałkowitego

Wiem, że mogę zdefiniować i zainicjować tego static constczłonka poza definicją klasy. Ponadto niestatyczny stały element członkowski danych można zainicjować na liście inicjatorów konstruktora.

Ale czy istnieje sposób na zainicjowanie stałej w deklaracji klasy, tak jak jest to możliwe w PHP lub C #?

Aktualizacja

Użyłem staticsłowa kluczowego tylko dlatego, że można było zainicjować takie stałe w deklaracji klasy w g ++. Potrzebuję tylko sposobu na zainicjowanie stałej w deklaracji klasy, bez względu na to, czy została zadeklarowana jako, staticczy nie.

ezpresso
źródło
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.To zły sposób decydowania, czy członek powinien być, staticczy nie. Nigdy nie pozwól, aby leksykalne lenistwo decydowało o semantyce twojego kodu.
Wyścigi lekkości na orbicie
That's the wrong way to decide whether a member should be static or not.Nie zgadzam się. Myślę, że to nie ma znaczenia dla stałych członków.
ezpresso
3
@expresso: Wcale nie. Możesz zainicjować niestały staticelement członkowski z informacjami specyficznymi dla instancji. To, że zdecydowałeś, że twoja stała jest właściwością typu, a nie konkretną instancją, jest powodem, dla którego to zrobisz static, a nie dlatego, że spodobał ci się skrót pisania.
Wyścigi lekkości na orbicie
@lightless: Cóż, jest to możliwe, ale nie widzę żadnego powodu, aby używać inicjalizacji tych samych stałych specyficznych dla instancji z różnymi wartościami. Kiedyś używałem do tego pól klas innych niż const!
ezpresso
4
Dlaczego, jeśli nigdy się nie zmieniają po utworzeniu obiektu? struct myType { const std::time_t instantiated; myType() : instantiated(std::time(0)) {} };Wszystko, co może być, const powinno być const; dotyczy to zarówno członków, jak statici osób niebędących staticczłonkami.
Wyścigi lekkości na orbicie

Odpowiedzi:

136

W języku C ++ 11 elementy niebędące staticdanymi , składowe danych i elementy static constexprczłonkowskie static constdanych typu integralnego lub wyliczeniowego mogą być inicjowane w deklaracji klasy. na przykład

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

W tym przypadku element iczłonkowski wszystkich wystąpień klasy Xjest inicjowany 5przez konstruktora generowanego przez kompilator, a element fczłonkowski jest inicjowany w 3.12. Element static constczłonkowski danych jjest zainicjowany 42, a element static constexprczłonkowski danych gjest zainicjowany 9.5.

Ponieważ floati doublenie są typu integralnego ani wyliczeniowego, takie elementy członkowskie muszą być constexprlub nie static, aby inicjator w definicji klasy był dozwolony.

Przed C ++ 11 tylko static constelementy członkowskie danych typu integralnego lub wyliczeniowego mogą mieć inicjatory w definicji klasy.

Anthony Williams
źródło
Czy istnieje link do części normy, w której zostało to określone? Właśnie zacząłem go używać (aczkolwiek kompilowałem jako C ++ 14) i jestem zachwycony, że działa, ponieważ oszczędzam dużo czasu, zlecając kompilatorowi to za mnie. Jednak dopóki nie przeczytałem Twojej odpowiedzi, nie byłem pewien, czy to powinno działać! Oficjalne słowo dodałoby mi pewności siebie, haha. Fwiw, ja też pracuję z członkami char const n[3]{'a', 'b', 'c'};.
podkreślenie_d
Zastanawiam się, dlaczego, static const intale static constexpr float? co to znaczy float and double are not of integral or enumeration type?
mrgloom
@mrgloom: tak, float i double nie są. Jeśli typ danych można przetłumaczyć na liczbę całkowitą, to jest to typ całkowity.
ppadhy
45

Inicjowanie statycznych zmiennych składowych innych niż stałe typy int nie jest standardem C ++ przed C ++ 11. Kompilator gcc nie ostrzeże Cię o tym (i mimo wszystko wygeneruje przydatny kod), chyba że określisz -pedanticopcję. Następnie powinien pojawić się błąd podobny do:

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]

Powodem tego jest to, że standard C ++ nie precyzuje, w jaki sposób powinny być implementowane zmiennoprzecinkowe i jest pozostawiony procesorowi. Aby obejść ten i inne ograniczenia, constexprwprowadzono.

Florian
źródło
14

Tak. Po prostu dodaj constexprsłowo kluczowe zgodnie z błędem.

StilesCrisis
źródło
1
Może nie chce wymagać C ++ 11 do swojego projektu?
Marc Mutz - mmutz
6
Problem, o którym wspomina, został wprowadzony ze względu na fakt, że próbował skompilować go jako C ++ 11, czyli chciałby obsługiwać C ++ 11 :)
Unknown1987
1

Jeśli potrzebujesz go tylko w jednej metodzie, możesz zadeklarować to lokalnie statycznie:

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
źródło
1

Napotkałem z tym prawdziwe problemy, ponieważ potrzebuję tego samego kodu do kompilacji z różnymi wersjami g ++ (kompilator GNU C ++). Musiałem więc użyć makra, aby zobaczyć, która wersja kompilatora jest używana, a następnie postępować zgodnie z tym

#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;

Spowoduje to użycie „const” dla wszystkiego przed wersją g ++ 6.0.0, a następnie „constexpr” dla wersji g ++ 6.0.0 i nowszych. To przypuszczenie w wersji jeżeli zmiana ma miejsce, ponieważ szczerze mówiąc nie zauważyłem tego momentu g ++ w wersji 6.2.1. Aby zrobić to dobrze, być może będziesz musiał spojrzeć na wersję pomocniczą i numer poprawki g ++, więc zobacz

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

aby uzyskać szczegółowe informacje na temat dostępnych makr.

W przypadku gnu możesz również trzymać się wszędzie używania „const”, a następnie kompilować z -fpermissiveflagą, ale to daje ostrzeżenia i lubię, aby moje rzeczy kompilowały się czysto.

Niezbyt dobre, ponieważ jest specyficzne dla kompilatorów gnu, ale podejrzewam, że można by było zrobić coś podobnego z innymi kompilatorami.

nilesOien
źródło