Krótka odpowiedź jest taka, że nie tylko jest static
przydatna, ale też całkiem dobrze będzie zawsze pożądana.
Po pierwsze, zauważ to static
i constexpr
są całkowicie od siebie niezależne. static
określa czas życia obiektu podczas wykonywania; constexpr
określa, że obiekt powinien być dostępny podczas kompilacji. Kompilacja i wykonanie są rozłączne i niejednoznaczne, zarówno w czasie, jak i przestrzeni. Po skompilowaniu program constexpr
przestaje być istotny.
Każda zadeklarowana zmienna constexpr
jest domyślnie, const
ale const
i static
prawie ortogonalna (z wyjątkiem interakcji z static const
liczbami całkowitymi).
Model C++
obiektowy (§ 1.9) wymaga, aby wszystkie obiekty inne niż pola bitowe zajmowały co najmniej jeden bajt pamięci i miały adresy; ponadto wszystkie takie obiekty, które w danym momencie można zaobserwować w programie, muszą mieć odrębne adresy (pkt 6). Nie wymaga to od kompilatora utworzenia nowej tablicy na stosie dla każdego wywołania funkcji z lokalną niestatyczną stałą tablicą const, ponieważ kompilator może schronić się w as-if
zasadzie, pod warunkiem, że może udowodnić, że żaden inny taki obiekt nie może być zauważony.
Niestety nie będzie to łatwe do udowodnienia, chyba że funkcja jest trywialna (na przykład nie wywołuje żadnej innej funkcji, której treść nie jest widoczna w jednostce tłumaczenia), ponieważ tablice, mniej więcej z definicji, są adresami. Dlatego w większości przypadków const(expr)
tablica niestatyczna będzie musiała zostać odtworzona na stosie przy każdym wywołaniu, co nie pozwala na obliczenie jej w czasie kompilacji.
Z drugiej strony, static const
obiekt lokalny jest wspólny dla wszystkich obserwatorów, a ponadto może zostać zainicjowany, nawet jeśli funkcja, w której jest zdefiniowany, nigdy nie zostanie wywołana. Żadne z powyższych postanowień nie ma zastosowania, a kompilator może nie tylko wygenerować tylko jedną jego instancję; wygenerowanie pojedynczego wystąpienia w pamięci tylko do odczytu jest bezpłatne.
Więc zdecydowanie powinieneś użyć static constexpr
w swoim przykładzie.
Jest jednak jeden przypadek, w którym nie chcesz używać static constexpr
. O ile constexpr
zadeklarowany obiekt nie jest używany ani deklarowany ODRstatic
, kompilator może go w ogóle nie uwzględniać. Jest to bardzo przydatne, ponieważ pozwala na użycie tymczasowych constexpr
tablic w czasie kompilacji bez zanieczyszczania skompilowanego programu niepotrzebnymi bajtami. W takim przypadku najwyraźniej nie chcesz używać static
, ponieważ static
prawdopodobnie zmusi obiekt do istnienia w czasie wykonywania.
const
zconst
obiektu, jedynie zconst X*
którego punkty doX
. Ale nie o to chodzi; chodzi o to, że obiekty automatyczne nie mogą mieć adresów statycznych. Jak powiedziałem,constexpr
przestaje mieć znaczenie po zakończeniu kompilacji, więc nie ma nic do odrzucenia (i całkiem możliwe, że w ogóle, ponieważ nie można zagwarantować, że obiekt istnieje w środowisku wykonawczym.)static
aconstexpr
jednak wyjaśnić, że są prostopadłe i niezależna, robi różne rzeczy. Następnie podajesz powód, aby NIE łączyć tych dwóch, ponieważ zignorowałoby to użycie ODR (co wydaje się przydatne). Aha i wciąż nie rozumiem, dlaczego statyczny powinien być używany z constexpr, ponieważ statyczny jest przeznaczony do środowiska wykonawczego. Nigdy nie wyjaśniłeś, dlaczego statyczny z constexpr jest ważny.static constexpr
(nie pozwala na odtworzenie stałej tablicy przy każdym wywołaniu funkcji), ale poprawiłem kilka słów, które mogłyby to uczynić bardziej zrozumiałym. Dzięki.constexpr
stała zmienna jest używana tylko w kontekstach czasu kompilacji i nigdy nie jest potrzebna w środowisku wykonawczym, tostatic
nie ma sensu, ponieważ do momentu przejścia do środowiska wykonawczego wartość została skutecznie „wstawiona”. Jeśli jednakconstexpr
zostanie użyty w kontekście środowiska wykonawczego (innymi słowy,constexpr
musiałby zostać przekonwertowany naconst
niejawnie i dostępny z fizycznym adresem dla kodu środowiska wykonawczego), będzie chciałstatic
zapewnić zgodność z ODR itp. Tak przynajmniej rozumiem.static constexpr int foo = 100;
. Nie ma powodu, dla którego kompilator nie mógłby zastąpićfoo
dosłownego użycia wszędzie100
, chyba że kod działałby podobnie&foo
. Więcstatic
onfoo
ma przydatność w tym przypadku, ponieważfoo
nie istnieje w czasie wykonywania. Znowu wszystko do kompilatora.Oprócz podanej odpowiedzi warto zauważyć, że kompilator nie jest wymagany do inicjalizacji
constexpr
zmiennej w czasie kompilacji, wiedząc, że różnica międzyconstexpr
istatic constexpr
polega na tym, że użyciestatic constexpr
zapewnia, że zmienna jest inicjowana tylko raz.Poniższy kod pokazuje, w jaki sposób
constexpr
zmienna jest inicjowana wiele razy (z tą samą wartością), a nastatic constexpr
pewno jest inicjowana tylko raz.Ponadto kod porównuje przewagę opcji
constexpr
przeciwconst
w połączeniu zstatic
.Możliwe wyjście programu:
Jak widać,
constexpr
inicjowany jest wiele razy (adres nie jest taki sam), astatic
słowo kluczowe zapewnia, że inicjalizacja jest wykonywana tylko raz.źródło
constexpr const short constexpr_short
do podania błędu, jeśli constexpr_short zostanie ponownie zainicjowanyconstexpr const
nie ma sensu, ponieważconstexpr
już jestconst
, dodawanieconst
raz lub wiele razy jest ignorowane przez kompilator. Próbujesz wychwycić błąd, ale to nie jest błąd, tak działa większość kompilatorów.