Chcę mieć static const
char
tablicę w mojej klasie. GCC narzekało i powiedziało mi, że powinienem skorzystać constexpr
, chociaż teraz mówi mi, że to niezdefiniowane odniesienie. Jeśli ustawię tablicę jako nie będącą członkiem, wówczas się kompiluje. Co się dzieje?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
c++
c++11
static-members
constexpr
Pubby
źródło
źródło
int
@MooingDuck. Działa dobrze, gdy nie jest członkiem. Czy to też nie naruszy zasady?int
s cheat. Jako nie będący członkiem nie powinno to być dozwolone, chyba że zasady ulegną zmianie dla C ++ 11 (możliwe)Odpowiedzi:
Dodaj do pliku CPP:
Powód: Musisz podać definicję elementu statycznego, a także deklarację. Deklaracja i inicjator wchodzą do definicji klasy, ale definicja elementu musi być osobna.
źródło
decltype(foo::baz) constexpr foo::baz;
C ++ 17 wprowadza zmienne wbudowane
C ++ 17 naprawia ten problem dla
constexpr static
zmiennych składowych wymagających definicji poza linią, jeśli była używana odr. Zobacz drugą połowę tej odpowiedzi, aby poznać szczegóły sprzed C ++ 17.Propozycja P0386 Zmienne wbudowane wprowadza możliwość zastosowania
inline
specyfikatora do zmiennych. W szczególności w tym przypadkuconstexpr
oznacza toinline
statyczne zmienne składowe. Wniosek mówi:i zmodyfikowano [basic.def] p2:
i dodaj [depr.static_constexpr] :
C ++ 14 i wcześniejsze
W C ++ 03 wolno nam udostępniać tylko inicjatory w klasie dla całek const lub typów wyliczania const , w C ++ 11 użycie
constexpr
tego rozszerzenia zostało rozszerzone na typy dosłowne .W C ++ 11 nie musimy podawać definicji zakresu przestrzeni nazw dla elementu statycznego,
constexpr
jeśli nie jest on używany odr , możemy to zobaczyć w części roboczej standardowej sekcji C ++ 119.4.2
[class.static.data], która mówi: ( podkreślenie moje idzie do przodu ):Pojawia się więc pytanie
baz
: używa się tutaj odr :a odpowiedź brzmi tak , dlatego potrzebujemy również definicji zakresu przestrzeni nazw.
Jak więc ustalić, czy zmienna jest używana odr ? Oryginalne sformułowanie C ++ 11 w sekcji
3.2
[basic.def.odr] mówi:Tak więc
baz
nie uzyskując ekspresję stały ale lwartość-to-rvalue konwersji nie jest natychmiast stosowany, ponieważ nie stosuje się z powodubaz
jest tablicą. Jest to omówione w sekcji4.1
[conv.lval], która mówi:Co jest stosowane w konwersji tablica na wskaźnik .
To sformułowanie [basic.def.odr] zostało zmienione z powodu Raportu Defektu 712, ponieważ niektóre przypadki nie były objęte tym sformułowaniem, ale zmiany te nie zmieniają wyników dla tego przypadku.
źródło
constexpr
absolutnie nie ma to z tym nic wspólnego? (i takbaz
jest wyrażeniem ciągłym)integral or enumeration type
ale inaczej, tak, ważne jest to, że jest to wyrażenie stałe .Jest to naprawdę wada w C ++ 11 - jak wyjaśnili inni, w C ++ 11 statyczna zmienna członkowska constexpr, w przeciwieństwie do każdego innego rodzaju zmiennej globalnej constexpr, ma zewnętrzne powiązanie, dlatego musi być gdzieś wyraźnie zdefiniowana.
Warto również zauważyć, że w praktyce często można uniknąć statycznych zmiennych składowych constexpr bez definicji podczas kompilacji z optymalizacją, ponieważ mogą one być wstawiane we wszystkich zastosowaniach, ale jeśli kompilujesz bez optymalizacji, często twój program nie będzie łączył się. To sprawia, że jest to bardzo popularna ukryta pułapka - twój program dobrze kompiluje się z optymalizacją, ale jak tylko wyłączysz optymalizację (być może w celu debugowania), nie łączy się.
Dobra wiadomość - ta usterka została naprawiona w C ++ 17! Podejście to jest jednak nieco skomplikowane: w C ++ 17 statyczne zmienne constexpr są domyślnie wbudowane . Po inline stosowany do zmiennych jest nową koncepcją w C ++ 17, ale w rzeczywistości oznacza, że nie trzeba nigdzie wyraźnej definicji.
źródło
Czy bardziej eleganckim rozwiązaniem nie jest zmiana
char[]
na:W ten sposób możemy mieć definicję / deklarację / inicjalizator w 1 linii kodu.
źródło
char[]
możesz użyć,sizeof
aby uzyskać długość łańcucha w czasie kompilacji, zchar *
nie możesz (zwróci szerokość typu wskaźnika, w tym przypadku 1).sizeof
problemu, i może być używana w rozwiązaniach „tylko w nagłówku”Moim obejściem dla zewnętrznego połączenia elementów statycznych jest użycie
constexpr
referencyjnych elementów pobierających (które nie napotykają na problem @gnzlbg podniesiony jako komentarz do odpowiedzi z @deddebme).Ten idiom jest dla mnie ważny, ponieważ nienawidzę posiadania wielu plików .cpp w moich projektach i staram się ograniczyć liczbę do jednego, który składa się wyłącznie z
#include
s imain()
funkcji.źródło
W moim środowisku wersja gcc ma wersję 5.4.0. Dodanie „-O2” może naprawić ten błąd kompilacji. Wygląda na to, że gcc może obsłużyć ten przypadek, gdy prosi o optymalizację.
źródło