Oba są stałe czas kompilacji. Ale możesz zrobić const_cast pierwszego i napisać do niego. Ale zostanie zoptymalizowany przez dowolny kompilator, ponieważ nie wpływa to na „odczyty”, tak jak dzieje się to w czasie kompilacji.
Bonita Montero,
Odpowiedzi:
347
Wierzę, że jest różnica. Zmieńmy ich nazwy, abyśmy mogli łatwiej o nich mówić:
Zarówno PI1i PI2są stałe, co oznacza, że nie można ich modyfikować. Jednak tylkoPI2 stała czasowa kompilacji. To powinno być inicjowane w czasie kompilacji. PI1może być zainicjowany w czasie kompilacji lub w czasie wykonywania. Ponadto, tylkoPI2 mogą być stosowane w kontekście, który wymaga stałego kompilacji. Na przykład:
Do czego powinieneś użyć? Używaj w zależności od potrzeb. Czy chcesz mieć pewność, że masz stałą czasową kompilacji, której można używać w kontekstach, w których wymagana jest stała czasowa kompilacji? Czy chcesz mieć możliwość zainicjowania go za pomocą obliczeń wykonanych w czasie wykonywania? Itp.
Jesteś pewny? Ponieważ const int N = 10; char a[N];działa, a granice tablic muszą być stałymi w czasie kompilacji.
fredoverflow
10
Jestem pewien, że jeśli chodzi o przykłady, które napisałem, przetestowałem każdy z nich przed opublikowaniem. Jednak mój kompilator pozwala mi przekonwertować PI1na stałą całkowania w czasie kompilacji do użycia w tablicy, ale nie do użycia jako parametr szablonu całki nietypowej. Tak więc PI1wydaje mi się, że konwertowanie na typ całkowy w czasie jest hitem.
Howard Hinnant,
34
@FredOverflow: Nieprzetworzone indeksy tablicowe „działały” przez około dekadę (istnieje na przykład rozszerzenie g ++), ale to nie znaczy, że jest to całkowicie legalne C ++ (chociaż niektóre nowsze standardy C lub C ++ sprawiły, że jest to legalne , ja zapomniałem który). Jeśli chodzi o różnice w stałych kompilacji, parametry szablonu i użycie jako enuminicjalizatora są jedynymi dwiema znaczącymi różnicami pomiędzy consti constexpr(i żadna z nich i tak nie działa double).
Damon,
17
Paragraf 4 5.19 Wyrażenia stałe [wyrażenie normalne] jest również (nienormatywną) uwagą, która słynie, że implementacja może wykonywać arytmetykę zmiennoprzecinkową inaczej (np. W odniesieniu do dokładności) w czasie kompilacji niż w czasie wykonywania. Więc 1 / PI1i 1 / PI2mogą dawać różne wyniki. Nie sądzę jednak, aby ta technika była tak ważna jak rada w tej odpowiedzi.
Luc Danton
4
Ale constexpr double PI3 = PI1;działa dla mnie poprawnie. (MSVS2013 CTP). Co ja robię źle?
NuPagadi
77
Nie ma tutaj różnicy, ale ma to znaczenie, gdy masz typ z konstruktorem.
struct S {constexpr S(int);};const S s0(0);constexpr S s1(1);
s0jest stały, ale nie obiecuje, że zostanie zainicjowany w czasie kompilacji. s1jest oznaczony constexpr, więc jest stałą, a ponieważ Skonstruktor jest również oznaczony constexpr, zostanie zainicjowany w czasie kompilacji.
W większości przypadków ma to znaczenie, gdy inicjalizacja w środowisku wykonawczym byłaby czasochłonna i chcesz przekazać tę pracę kompilatorowi, gdzie jest to również czasochłonne, ale nie spowalnia czasu wykonywania skompilowanego programu
Zgadzam się: doszedłem do wniosku, constexprże doprowadziłoby to do diagnozy, gdyby obliczenie obiektu w czasie kompilacji było niemożliwe. Mniej jasne jest to, czy funkcja oczekująca stałego parametru może być wykonana w czasie kompilacji, czy parametr powinien być zadeklarowany jako, consta nie jako constexpr: tzn. Czy constexpr int foo(S)byłby wykonywany w czasie kompilacji, jeśli wywołam foo(s0)?
Matthieu M.,
4
@MatthieuM: Wątpię, czy foo(s0)byłby wykonywany w czasie kompilacji, ale nigdy nie wiadomo: kompilator może wykonywać takie optymalizacje. Z pewnością ani gcc 4.7.2, ani clang 3.2 nie pozwalają mi na kompilacjęconstexpr a = foo(s0);
rici
50
constexpr wskazuje wartość, która jest stała i znana podczas kompilacji. const wskazuje wartość, która jest tylko stała; wiedza nie jest obowiązkowa podczas kompilacji.
int sz;constexprauto arraySize1 = sz;// error! sz's value unknown at compilation
std::array<int, sz> data1;// error! same problemconstexprauto arraySize2 =10;// fine, 10 is a compile-time constant
std::array<int, arraySize2> data2;// fine, arraySize2 is constexpr
Zauważ, że const nie daje takiej samej gwarancji jak constexpr, ponieważ obiekty const nie muszą być inicjowane wartościami znanymi podczas kompilacji.
int sz;constauto arraySize = sz;// fine, arraySize is const copy of sz
std::array<int, arraySize> data;// error! arraySize's value unknown at compilation
Wszystkie obiekty constexpr są const, ale nie wszystkie obiekty constexpr są constexpr.
Jeśli chcesz, aby kompilatory gwarantowały, że zmienna ma wartość, której można użyć w kontekstach wymagających stałych czasowych kompilacji, narzędziem, do którego należy sięgnąć, jest constexpr, a nie const.
Bardzo podobało mi się twoje wyjaśnienie. Czy mógłbyś skomentować więcej gdzie są przypadki, w których możemy potrzebować użyć stałych czasowych kompilacji w rzeczywistych scenariuszach
Constexpr stałe symboliczne należy podać wartość, która jest znany w czasie kompilacji. Na przykład:
constexprint max =100;void use(int n){constexprint c1 = max+7;// OK: c1 is 107constexprint c2 = n+7;// Error: we don’t know the value of c2// ...}
Aby obsłużyć przypadki, w których wartość „zmiennej”, która jest inicjowana wartością nieznaną w czasie kompilacji, ale nigdy się nie zmienia po inicjalizacji, C ++ oferuje drugą formę stałej ( const ). Na przykład:
constexprint max =100;void use(int n){constexprint c1 = max+7;// OK: c1 is 107constint c2 = n+7;// OK, but don’t try to change the value of c2// ...
c2 =7;// error: c2 is a const}
Takie „ stałe zmienne” są bardzo powszechne z dwóch powodów:
C ++ 98 nie miał constexpr, więc ludzie używali const .
Element listy „Zmienne”, które nie są stałymi wyrażeniami (ich wartość nie jest znana w czasie kompilacji), ale nie zmieniają wartości po inicjalizacji, same w sobie są bardzo przydatne.
Odniesienie: „Programowanie: zasady i praktyka przy użyciu C ++” Stroustrup
Odpowiedzi:
Wierzę, że jest różnica. Zmieńmy ich nazwy, abyśmy mogli łatwiej o nich mówić:
Zarówno
PI1
iPI2
są stałe, co oznacza, że nie można ich modyfikować. Jednak tylkoPI2
stała czasowa kompilacji. To powinno być inicjowane w czasie kompilacji.PI1
może być zainicjowany w czasie kompilacji lub w czasie wykonywania. Ponadto, tylkoPI2
mogą być stosowane w kontekście, który wymaga stałego kompilacji. Na przykład:ale:
i:
ale:
Do czego powinieneś użyć? Używaj w zależności od potrzeb. Czy chcesz mieć pewność, że masz stałą czasową kompilacji, której można używać w kontekstach, w których wymagana jest stała czasowa kompilacji? Czy chcesz mieć możliwość zainicjowania go za pomocą obliczeń wykonanych w czasie wykonywania? Itp.
źródło
const int N = 10; char a[N];
działa, a granice tablic muszą być stałymi w czasie kompilacji.PI1
na stałą całkowania w czasie kompilacji do użycia w tablicy, ale nie do użycia jako parametr szablonu całki nietypowej. Tak więcPI1
wydaje mi się, że konwertowanie na typ całkowy w czasie jest hitem.enum
inicjalizatora są jedynymi dwiema znaczącymi różnicami pomiędzyconst
iconstexpr
(i żadna z nich i tak nie działadouble
).1 / PI1
i1 / PI2
mogą dawać różne wyniki. Nie sądzę jednak, aby ta technika była tak ważna jak rada w tej odpowiedzi.constexpr double PI3 = PI1;
działa dla mnie poprawnie. (MSVS2013 CTP). Co ja robię źle?Nie ma tutaj różnicy, ale ma to znaczenie, gdy masz typ z konstruktorem.
s0
jest stały, ale nie obiecuje, że zostanie zainicjowany w czasie kompilacji.s1
jest oznaczonyconstexpr
, więc jest stałą, a ponieważS
konstruktor jest również oznaczonyconstexpr
, zostanie zainicjowany w czasie kompilacji.W większości przypadków ma to znaczenie, gdy inicjalizacja w środowisku wykonawczym byłaby czasochłonna i chcesz przekazać tę pracę kompilatorowi, gdzie jest to również czasochłonne, ale nie spowalnia czasu wykonywania skompilowanego programu
źródło
constexpr
że doprowadziłoby to do diagnozy, gdyby obliczenie obiektu w czasie kompilacji było niemożliwe. Mniej jasne jest to, czy funkcja oczekująca stałego parametru może być wykonana w czasie kompilacji, czy parametr powinien być zadeklarowany jako,const
a nie jakoconstexpr
: tzn. Czyconstexpr int foo(S)
byłby wykonywany w czasie kompilacji, jeśli wywołamfoo(s0)
?foo(s0)
byłby wykonywany w czasie kompilacji, ale nigdy nie wiadomo: kompilator może wykonywać takie optymalizacje. Z pewnością ani gcc 4.7.2, ani clang 3.2 nie pozwalają mi na kompilacjęconstexpr a = foo(s0);
constexpr wskazuje wartość, która jest stała i znana podczas kompilacji.
const wskazuje wartość, która jest tylko stała; wiedza nie jest obowiązkowa podczas kompilacji.
Zauważ, że const nie daje takiej samej gwarancji jak constexpr, ponieważ obiekty const nie muszą być inicjowane wartościami znanymi podczas kompilacji.
Wszystkie obiekty constexpr są const, ale nie wszystkie obiekty constexpr są constexpr.
Jeśli chcesz, aby kompilatory gwarantowały, że zmienna ma wartość, której można użyć w kontekstach wymagających stałych czasowych kompilacji, narzędziem, do którego należy sięgnąć, jest constexpr, a nie const.
źródło
Constexpr stałe symboliczne należy podać wartość, która jest znany w czasie kompilacji. Na przykład:
Aby obsłużyć przypadki, w których wartość „zmiennej”, która jest inicjowana wartością nieznaną w czasie kompilacji, ale nigdy się nie zmienia po inicjalizacji, C ++ oferuje drugą formę stałej ( const ). Na przykład:
Takie „ stałe zmienne” są bardzo powszechne z dwóch powodów:
Odniesienie: „Programowanie: zasady i praktyka przy użyciu C ++” Stroustrup
źródło