2015-09-23 75 views
7

I zostały szczotkowanie na koncepcji tablicy w C++, gdy natknąłem się na to pytanie: Return an array in c++Składnia powrocie odwołanie do tablicy w C++

Ktoś odpowiedział stosując tę ​​deklarację:

int (&f(int (&arr)[3]))[3] 

Co mogę Wydaje się, że po prostu chwyta [3] po zamykającej się parentezji. Nigdy nie widziałem deklaracji funkcji, która wyglądałaby tak. Rozumiem resztę składni, ale nie rozumiem szczególnie, jak działa [3], ponieważ jest po nazwie funkcji. Przepraszam z góry, jeśli przeoczę coś prostego. Próbowałem również spojrzeć na specyfikację dla deklaracji funkcji, ale nie widziałem niczego, co mógłbym powiązać ze składnią indeksu dolnego. Jak to możliwe?

+1

Ponieważ K & R są sadystów . – orlp

+0

http://cdecl.org/. Składnia C deklaratora może być "zabawna". –

+1

Kiedy już to zrobisz, nigdy nie pisz tak okropnej rzeczy w prawdziwym kodzie. – kebs

Odpowiedz

5

Funkcja zwraca się odniesienie do tablicy int wielkości 3 i [3] część po funkcji właściwie rozmiar tablicy być zwrócony w odniesieniu.

Ta składnia niejasne pochodzi z dziwnej składni deklaracji tablicy, co robisz, jak:

int arr[3]; //real but weird 

Językiem byłoby znacznie prostsze, gdyby miało to zamiast:

int[3] arr; //hypothetical but better and simpler 

ponieważ rozmiar 3 to część typu arr, więc ma sens, jeśli wszystkie części pojawiają się po lewej stronie nazwy zmiennej, w taki sam sposób jak przy pisaniu:

unsigned int a; 

You dont napisać:

unsigned a int; //analogous to : int a [3]; 

Więc podczas gdy język robi słusznie z unsigned int, robi się bardzo dziwne rzeczy z int[3].

Teraz wracając do deklaracji funkcji, funkcja byłoby znacznie lepiej, gdyby została zadeklarowana jako:

int[3]& f(int[3]& arr); //hypothetical 

tylko gdyby wszystkie części po lewej stronie nazwy zmiennej. Ale ponieważ nie robi (tzn języku wymaga pisanie rozmiar na skrajnej prawej stronie po nazwie zmiennej), skończyć z tym dziwnym Podpis:

int (&f(int (&arr)[3])[3]; //real 

Zauważ, że jak nawet parametr staje się dziwny.


Ale można uprościć go typedef jak:

typedef int array_type[3]; 

array_type& f(array_type& arr); 

który wygląda znacznie lepiej. Teraz tylko typ typedef wygląda dziwnie.

z C++ 11, można napisać nawet lepsze typedef:

using array_type = int[3]; 

array_type& f(array_type& arr); 

który jest tak blisko, jak to (jeśli wizualizować array_type jak int[3]):

int[3]& f(int[3]& arr); //hypothetical 

nadzieję, że pomoże .

+1

Wielkie dzięki! To wszystko ma teraz sens. – Izodine

1
int (&f (int (&arr)[3]))[3] 
{ 
    return arr; 
} 

Chodźmy od środka. Część (int (&arr)[3]) oznacza, że ​​funkcja przyjmuje jako odniesienie odwołanie do tablicy int złożonej z 3 elementów. To jest ten sam arr, który jest zwracany.

Pozostała część składni, która powinna zwracać odwołanie do tablicy składającej się z 3 elementów. f to nazwa funkcji. int &f() oznaczałoby, że zwrócisz referencję do int, ale musisz zwrócić tablicę o stałym rozmiarze, dlatego powinieneś dodać [3] Użyto nawiasu wokół &f(...), ponieważ w C++ wpisujesz rozmiar tablicy po zmiennej nazwa, nie po nazwie typu, jak wyjaśnia Nawaz w swojej odpowiedzi. Jeśli pominąć te nawiasy, kompilator zinterpretuje f jako tablicę odniesień, jak w int & f[3].

2

Proszę przeczytać tę article o historii C, przez jej twórcę Dennisa M. Ritchiego.

wygląda następująco składni dostaliśmy od B, thru C do C++ ...

wierzę, w ten sposób deklarowania kilku zmiennych pewnego rodzaju „podstawowego” jest pierwiastkiem tej dziwnej składni:

int a, *b, c[3]; 

Więc a tylko int, b jest wskaźnikiem do int i c jest tablicą int ...

Jeśli te * i [3] będzie częścią definicji typu nie zmienna definicja - wtedy musiałby 3 linie napisać to:


Są dwa dobre środki na SO C page nauczyć się analizować takie konstrukcje:

cdecl zawodzi trochę - bo referencje nie są częścią C - więc spróbujmy z The Clockwise/Spiral Rule for parsing C declarations.

int (&f(int (&arr)[3]))[3] 
  1. arr -> arr
  2. &arr -> arr jest odniesienie
  3. (&arr) -> arr jest odniesieniem - otoczenie () tak naprawdę nic nie zmienia
  4. (&arr)[3] -> ARR jest odniesienie do tablicy wielkości 3
  5. int (&arr)[3] -> ARR jest odniesienie do tablicy wielkości 3 typu Int

Dalej:

  1. f -> f
  2. f(int (&arr)[3]) -> f to funkcja zajmująca się arr, która jest ...
  3. &f(int (&arr)[3]) -> f to funkcja zajmująca się arr, która jest ... i powracająca referencja
  4. (&f(int (&arr)[3]))[3] -> f Funkcja przy ARR który ... i powrót odniesienie do tablicy wielkości 3
  5. int (&f(int (&arr)[3]))[3] -> f to funkcja podejmowania ARR, która jest odniesienie do tablicy wielkości 3 typu int i powrocie odniesienie do tablicy o rozmiarze 3 typu int

oczywiście - kod ten powinien zostać zastąpiony, przynajmniej dla mnie, dużo łatwiejsze do odczytania wersję:

using int3 = int[3]; 
int3& f(int3& arr); 
+0

Ma to sens. Dzięki za szczegółową odpowiedź! Zasada spiralna będzie bardzo przydatna. – Izodine