2015-01-22 9 views
10

W następującym kodzie C++ 11, ostatnie wywołanie arraySize powoduje błąd kompilacji. Najwyraźniej dzieje się tak, ponieważ y jest tablicą runtime, a parametr arraySize N nie może zostać wyprowadzony dla y. Nie rozumiem, dlaczego x jest tablicą o rozmiarze czasu kompilacji, ale kończy się na rozmiarze runtime. Funkcja szablonu arraySize jest pobierana bezpośrednio z Scott Meyers' «Efektywne Nowoczesne C++» Pozycja 1.C++: Dlaczego ten constexpr nie jest stałą czasową kompilacji

#include <cstddef> 

template<typename T, std::size_t N> 
constexpr std::size_t arraySize(T(&)[N]) noexcept { return N; } 

struct S 
{ 
    char c[10]; 
}; 

int main() 
{ 
    S s; 
    S* ps = &s; 

    char x[arraySize(s.c)]; 
    char y[arraySize(ps->c)]; // why is y a runtime sized array? 

    arraySize(x); 
    arraySize(y); // error !? 

    return 0; 
} 
+0

Jaki jest błąd? –

+0

Cóż, jestem zakłopotany ... zarówno 'sc' jak i' ps-> c' mają ten sam typ, jak wypisane przez 'typeid (sc) .name()' lub 'typeid (ps-> c) .name() ', ie'A10_c' dla g ++ – vsoftco

+0

Aby przyciemnić nieco więcej: jeśli ustawisz parametr' arraySize() na wartość referencyjną na const, poniższe kompiluje: 'char y [arraySize (decltype (ps-> c) {})]; ' – Quentin

Odpowiedz

9

W C++, błąd nie jest wezwanie do arraySize(y), ale deklaracja samego y.

Ograniczenia w deklaracji tablicowej muszą być "przekształconym wyrażeniem stałym".

Jeśli Twój kompilator akceptuje deklarację y, a później mówi, że y jest tablicą powiązanego środowiska wykonawczego, to nie jest kompilatorem C++. W żadnej ratyfikowanej wersji C++ nie ma tablic wykonawczych, ani aktualnej wersji roboczej.

Istotna różnica między arraySize(s.c) i arraySize(ps->c) jest ps->c jest taka sama jak (*ps).c i operator * nieprawidłowego wymaga lwartości do RValue konwersję ps, który nie jest stały wyrażenie (ani &s patrz poniżej). Reszta wyrażenia nie obejmuje konwersji l-do-wartości, tablica-wartość jest bezpośrednio związana przez odniesienie.

Stała ekspresja jest albo rdzeń glvalue stała ekspresja którego wartość odnosi się do jednostki, która jest dopuszczalna wynikiem stałej ekspresji (jak zdefiniowane poniżej) albo prvalue rdzeń stałej ekspresji, którego wartość jest obiekt, gdzie dla tego obiektu i jego podobiektów:

  • każdy niż statyczna składowa danych typu odniesienia dotyczy jednostki, która jest dopuszczalna wynikiem stałej ekspresji i

  • i f obiekt lub podobiekt jest typu wskaźnikowego, zawiera adres obiektu o statycznym czasie przechowywania, adres znajdujący się za końcem takiego obiektu (5.7), adres funkcji lub wartość wskaźnika zerowego.

Podmiot jest dozwolone wynikiem stałej ekspresji, jeśli celem statycznych okres przechowywania, który jest albo nie jest tymczasowe przedmiot lub stanowi tymczasowy obiekt, którego wartość satis fi ES powyższe ograniczenia, lub jest Funkcja.

Wyraźnie ps zawiera adres obiektu z automatycznym okres przechowywania, więc nie może być uznana za constexpr. Ale wszystko powinno zacząć działać, jeśli zmienisz S s; S* ps = &s; do static S s; constexpr S* ps = &s;

(Z drugiej strony, można by pomyśleć, że parametr do arraySize(s.c) nie jest stałą albo wyrażenie, gdyż jest punktem odniesienia, a nie przedmiot pamięci statycznej czas trwania)

+0

"Jeśli twój kompilator zaakceptuje deklarację y, a następnie powie ci, że y jest tablicą powiązanego środowiska wykonawczego, to nie jest kompilatorem C++." - Ta część nie jest prawdziwa, zgodna implementacja (kompilator) jest wyraźnie dopuszczalna do "akceptowania" (to znaczy kompilowania i/lub wykonywania) źle sformułowanych danych wejściowych przez sposób rozszerzenia, patrz np. 1.4/8 w projekcie N4296. Implementacja ** jest ** wymagana do wydania diagnostyki w tym przypadku, a GCC i clang nie wydają ostrzeżenia dla tablic o zmiennej długości, chyba że do wywołania zostanie dodana opcja -pedantic. –