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 przypadku constexpr
oznacza to inline
statyczne zmienne składowe. Wniosek mówi:
Specyfikator wbudowany można zastosować zarówno do zmiennych, jak i do funkcji. Zmienna zadeklarowana inline ma taką samą semantykę jak funkcja zadeklarowana inline: może być zdefiniowana identycznie w wielu jednostkach translacji, musi być zdefiniowana w każdej jednostce translacji, w której jest używana odr, a zachowanie programu wygląda tak, jakby jest dokładnie jedna zmienna.
i zmodyfikowano [basic.def] p2:
Deklaracja jest definicją, chyba że
...
- deklaruje element danych statycznych poza definicją klasy, a zmienna została zdefiniowana w klasie za pomocą specyfikatora constexpr (to użycie jest przestarzałe; patrz [depr.static_constexpr]),
...
i dodaj [depr.static_constexpr] :
W celu zapewnienia zgodności z wcześniejszymi międzynarodowymi standardami C ++ element statyczny constexpr może być redundantnie nadpisany poza klasą bez inicjatora. To użycie jest przestarzałe. [Przykład:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
- koniec przykładu]
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 ++ 11 9.4.2
[class.static.data], która mówi: ( podkreślenie moje idzie do przodu ):
[...] Statyczny element danych typu literalnego można zadeklarować w definicji klasy za pomocą specyfikatora constexpr; jeżeli tak, w jej deklaracji należy podać inicjator nawiasowy lub równy, w którym każda klauzula inicjalizująca, która jest wyrażeniem przypisania, jest wyrażeniem stałym. [Uwaga: W obu tych przypadkach element członkowski może pojawiać się w stałych wyrażeniach. —Wskazówka]
Członek nadal będzie zdefiniowany w zakresie przestrzeni nazw, jeśli jest używany w programie odr (3.2) , a definicja zakresu przestrzeni nazw nie będzie zawierała inicjatora.
Pojawia się więc pytanie baz
: używa się tutaj odr :
std::string str(baz);
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:
Wyrażenie jest potencjalnie oceniane, chyba że jest nieocenionym operandem (klauzula 5) lub jego podwyrażeniem. Zmienna, której nazwa pojawia się jako potencjalnie ocenione wyrażenie, jest używana odr, chyba
że jest to obiekt, który spełnia wymagania dotyczące pojawienia się w wyrażeniu stałym (5.19) i natychmiast stosowana jest konwersja wartości z wartości na wartość (4.1) .
Tak więc baz
nie uzyskując ekspresję stały ale lwartość-to-rvalue konwersji nie jest natychmiast stosowany, ponieważ nie stosuje się z powodu baz
jest tablicą. Jest to omówione w sekcji 4.1
[conv.lval], która mówi:
Glvalue (3.10) niefunkcjonalnego typu innego niż tablica T można przekonwertować na wartość 53. [...]
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.