2013-08-09 16 views
10

Dlaczego między tablicami występuje różnica między const a constexpr?Różnica między tablicami const i constexpr

int const xs[]{1, 2, 3}; 
constexpr int ys[]{1, 2, 3}; 

int as[xs[0]]; // error. 
int bs[ys[0]]; // fine. 

byłoby oczekiwać, zarówno xs[0] i ys[0] być wyrażeniami ale tylko ten ostatni jest traktowany.

+0

Wartości 'constexpr' mogą być obliczane w czasie kompilacji,' const' nie może. –

+3

Tablica 'const' nie musi mieć widocznej definicji (może być" zewnętrzna "lub może być zdefiniowana później w pliku), dlatego istnieje różnica.Jeśli chodzi o powód, dla którego istnieje * ta * różnica, podejrzewam, że standard * mógł * powiedzieć, że gdy definicja jest obecna i jest odpowiednia dla 'constexpr' (jak w twoim kodzie), to' const' jest tak dobre jak 'constexpr', ponieważ w takim przypadku informacja jest dostępna do oceny. Nie wiem, dlaczego tak nie mówi, ale równie dobrym powodem jest "jeśli chcesz, żeby twoja tablica była" constexpr ", po prostu powiedz". –

+0

Dodatek, który prawdopodobnie byłby dodatkiem do ciebie pytanie, wydaje mi się, dlaczego 'int const x = 5;' i 'constexpr int y = 5' może * oba * są używane jako wielkości stałe wielkości decybelów tablicy (' int a [x], b [y] '), ale wewnątrz tablic, które są traktowane inaczej (co odpowiada komentarzowi Steve'a powyżej). – WhozCraig

Odpowiedz

5

dłuższy komentarz jako wspólnotowe wiki.


Wyrażenie xs[0] jest zdefiniowana w [expr.sub]/1 jako *((xs)+(0)). (Patrz poniżej dla nawiasach.)

jedno z wyrażeń ma typu „wskaźnik do T”, a drugi ma unscoped wyliczenie lub integralną typu.

Zatem macierz konwersji do wskaźnika [conv.array] stosuje się:

lwartością lub RValue typu „matrycy z N T” lub „matrycy o nieznanym granica T” Można zostać przekonwertowane na wartość typu "wskaźnik do T". Wynikiem jest wskaźnik do pierwszego elementu tablicy.

Uwaga może działać na lwartością a wynik jest prvalue, 0 jako liczba całkowita dosłownym jest prvalue również. Dodawanie jest zdefiniowane w [expr.add]/5. Ponieważ oba są prvalues ​​, nie jest wymagana żadna konwersja na wartość l do wartości r.

int arr[3]; 
constexpr int* p = arr; // allowed and compiles 

Kluczowym krokiem wydaje się być pośredniość * [expr.unary.op]/1

jednoargumentowych * operator wykonuje pośredniość: określenie, do której jest stosowana, jako wskaźnik do typu obiektu lub wskaźnika do typu funkcji, a wynikiem jest lwartość odnosząca się do obiektu lub funkcji, której dotyczy wyrażenie.

więc wynikiem xs[0] jest lwartością odnosząc się do pierwszego elementu tablicy xs i jest typu int const.


N.B. [expr.prim.general]/6

Wyrażenie parentetyzowane jest wyrażeniem pierwotnym, którego typ i wartość są identyczne z wyrażeniem zamkniętym. Obecność nawiasów nie wpływa na to, czy wyrażenie jest wartością l.


Jeśli teraz spojrzeć na kulami w [expr.const]/2, który uniemożliwi pewne wyrażenia i konwersje do stawienia się w stałych wyrażeń, jedyny pocisk, który mógłby ubiegać (AFAIK) jest lwartość-do -rvalue konwersja:

  • konwersja lwartość do RValue (4,1), o ile jest on stosowany do

    • nieulotna wartość gloduła typu całkowego lub wyliczeniowego, która odnosi się do nielotnego obiektu const z poprzedzającą inicjacją, inicjalizowanego wyrażeniem stałym [Uwaga: literał łańcuchowy (2.14.5) odpowiada tablicy takich obiektów. końcem uwaga] lub

    • nielotny glvalue dosłownego typu, który odnosi się do trwałej przedmiotu zdefiniowanego w constexpr lub że odnosi się do pod-obiekt takiego obiektu lub

[...]

jednak tylko prawdziwy konwersji lwartość do RValue zgodnie (4,1) (nie 4.2, w którym jest tablica do wskaźnika), który pojawia się w ocenie xs[0] jest konwersją z wynikowego l wartość odnosząca się do pierwszego elementu.

Dla przykładu w PO:

int const xs[]{1, 2, 3}; 
int as[xs[0]]; // error. 

Element xs[0] zawiera nielotny const integralną typu jego inicjalizacji poprzedza stałej ekspresji w których występuje, a została zainicjowana stałej ekspresji.


Nawiasem mówiąc, dodana „informacja” w cytowanym fragmencie [expr.const]/2 has been added to clarify że jest prawne:

constexpr char c = "hello"[0]; 

Należy zauważyć, że łańcuch jest dosłownym lwartością jako dobrze.


Byłoby wspaniale, gdyby ktoś (może zmienić to) wyjaśnić dlaczego xs[0] nie może pojawić się w stałej ekspresji.

+0

Powodem, dla którego implementacje odrzuca to, ponieważ nie wierzą, że jest to celem paragrafów (intencją było być kompatybilne wstecz z C++ 03, a C++ 03 nie pozwalało na 'const int a = 0; int x [a];'). C++ 03 również nie zezwalało na '" foo "[0]', ale że ten paragraf pozwala również na to, aby jeden był późniejszy "pozwól nam zostać, aby zmiany nienormatywne" zmienił się do wersji roboczej, ponieważ uznano za pożądane przeczytanie z literałów ciągowych. W efekcie, rzeczywistą intencją bulletu 1 wydaje się być "zezwolenie na czytanie z literału ciągłego i zachowanie zgodności wstecznej z C++ 03". –

+0

(powyższa uwaga jest oparta na wcześniejszej rozmowie z Richardem Smith'em, który zaimplementował ocenę constexpr dla klang). –

+0

@ JohannesSchaub-litb Nie jestem pewien, czy w pełni rozumiem twoje wyjaśnienie. 'const int a = 1; int x [a]; 'jest dozwolone w C++ 03 dla wszystkich, których znam, a tablice o zerowej wielkości są zabronione w C++ 03 i C++ 11. Dodatkowo, w jaki sposób rozluźnienie ograniczeń zmiennych "const" pojawiających się w wyrażeniach stałych zakłóca kompatybilność wsteczną? Czy to znaczy, że nie chodziło o * zmianę * niczego dla zmiennych "const"? – dyp

0

C++ 11 constexpr służy do włączania wyrażenia podczas kompilacji, w przeciwieństwie do słowa kluczowego const.

constexpr int ys[]{1, 2, 3}; jest oceniana w czasie kompilacji, więc nie ma błędu

gdy jest używany ys[0].

Zauważ też, C++ inicjalizacji 11 uniform jest tutaj stosowane z {}

Inny przykład:

constexpr int multipletwo(int x) 
{ 
return 2*x; 
} 

int num_array[multipletwo(3)]; //No error since C++11, num_array has 6 elements. 
Powiązane problemy