Poniższy kod nie jest kompilowany.
int a = 1, b = 2, c = 3;
int& arr[] = {a,b,c,8};
Co na ten temat mówi standard C ++?
Wiem, że mógłbym zadeklarować klasę, która zawiera odniesienie, a następnie utworzyć tablicę tej klasy, jak pokazano poniżej. Ale naprawdę chcę wiedzieć, dlaczego powyższy kod się nie kompiluje.
struct cintref
{
cintref(const int & ref) : ref(ref) {}
operator const int &() { return ref; }
private:
const int & ref;
void operator=(const cintref &);
};
int main()
{
int a=1,b=2,c=3;
//typedef const int & cintref;
cintref arr[] = {a,b,c,8};
}
Możliwe jest użycie struct cintref
zamiast const int &
symulacji tablicy odniesień.
intlink value = 8;
działa. sprawdź, czy nie wierzysz.operator=
. Nie można ponownie umieścić referencji. Jeśli naprawdę chcesz takie semantykę - choć nie osobiście znaleźć sytuację, w której jesteśmy praktycznie użyteczne - tostd::reference_wrapper
byłby sposób, aby to zrobić, jak to faktycznie przechowuje wskaźnik ale zapewnia odniesienie podobnyoperator
s i nie pozwalają ponownym zainstalowaniu. Ale wtedy użyłbym tylko wskaźnika!Odpowiedzi:
Odpowiadając na Twoje pytanie o standard mogę przytoczyć C ++ Standard §8.3.2 / 4 :
źródło
struct
którego jedyny element członkowski jest referencją. Ale robiąc to, masz teraz zarówno odniesienie, jak i obiekt nadrzędny do nazwania ... co teraz oznacza, że możesz jednoznacznie określić, który adres chcesz. Wydaje się oczywiste ... więc dlaczego nikt tego nie powiedział?What more is there to say?
A uzasadnienie dlaczego ? Chodzi mi wyłącznie o Standard, ale samo jego cytowanie i założenie, że to koniec dyskusji wydaje się pewnym sposobem na zniszczenie krytycznej myśli użytkowników - wraz z wszelkimi możliwościami ewolucji języka, np. Poza ograniczeniami pominięcia lub sztuczne ograniczenie. Jest ich za dużo ”, ponieważ norma mówi„ tutaj - i za mało ”i bardzo rozsądne jest to powiedzieć z następujących powodów praktycznych”. Nie wiem, dlaczego głosy za napływają tak chętnie do odpowiedzi, które mówią tylko o pierwszym, nawet nie próbując zgłębiać drugiego.Referencje nie są obiektami. Nie mają własnego magazynu, po prostu odwołują się do istniejących obiektów. Z tego powodu tablice odwołań nie mają sensu.
Jeśli potrzebujesz lekkiego obiektu, który odwołuje się do innego obiektu, możesz użyć wskaźnika. Będziesz mógł używać elementu
struct
referencyjnego jako obiektów w tablicach tylko wtedy, gdy podasz jawną inicjalizację dla wszystkich członków odniesienia dla wszystkichstruct
instancji. Odwołania nie mogą być inicjalizowane domyślnie.Edycja: Jak zauważa jia3ep, w standardowej sekcji dotyczącej deklaracji istnieje wyraźny zakaz dotyczący tablic odniesień.
źródło
To interesująca dyskusja. Oczywiście tablice referencji są całkowicie nielegalne, ale IMHO powód, dla którego nie jest tak prosty, jak powiedzenie „nie są obiektami” lub „nie mają rozmiaru”. Zwróciłbym uwagę, że same tablice nie są pełnoprawnymi obiektami w C / C ++ - jeśli sprzeciwiasz się temu, spróbuj utworzyć instancję niektórych klas szablonów stl, używając tablicy jako parametru szablonu „class” i zobacz, co się stanie. Nie możesz ich zwrócić, przypisać, przekazać jako parametry. (parametr tablicy jest traktowany jako wskaźnik). Ale tworzenie tablic tablic jest legalne. Referencje mają rozmiar, który kompilator może i musi obliczyć - nie możesz sizeof () odwołania, ale możesz utworzyć strukturę zawierającą tylko odwołania. Będzie miał wystarczający rozmiar, aby pomieścić wszystkie wskaźniki, które implementują odwołania. Możesz'
W rzeczywistości możesz dodać tę linię do definicji struktury
... a teraz mam coś, co wygląda DUŻO jak tablica referencji:
Otóż, to nie jest prawdziwa tablica, to przeciążenie operatora; nie zrobi rzeczy, które normalnie robią tablice, na przykład sizeof (arr) / sizeof (arr [0]). Ale robi dokładnie to, czego chcę, aby robił szereg odniesień, z całkowicie legalnym C ++. Z wyjątkiem (a) uciążliwa jest konfiguracja dla więcej niż 3 lub 4 elementów i (b) wykonuje obliczenia przy użyciu zestawu?: Co można zrobić za pomocą indeksowania (nie przy normalnym indeksowaniu semantyki wskaźnika C , ale mimo to indeksowanie). Chciałbym zobaczyć bardzo ograniczony typ „tablicy referencyjnej”, który faktycznie może to zrobić. To znaczy tablica odniesień nie byłaby traktowana jako ogólna tablica rzeczy, które są referencjami, ale raczej byłaby nową „tablicą referencji” zrobić za pomocą szablonów).
to prawdopodobnie zadziała, jeśli nie masz nic przeciwko temu rodzajowi nieprzyjemności: przetwórz '* this' jako tablicę int * i zwróć referencję utworzoną z jednego: (niezalecane, ale pokazuje jak właściwa 'tablica' pracowałbym):
źródło
std::tuple<int&,int&,int&> abcref = std::tie( a,b,c)
- który tworzy „tablicę” referencji, które mogą być indeksowane tylko przez stałe czasu kompilacji przy użyciu std :: get. Ale nie możesz tego zrobićstd::array<int&,3> abcrefarr = std::tie( a,b,c)
. Może jest na to sposób, o którym nie wiem. Wydaje mi się, że powinien istnieć sposób na napisanie specjalizacji,std::array<T&,N>
która może to zrobić - a więc funkcja,std::tiearray(...)
która zwraca jedną taką (lub zezwalastd::array<T&,N>
na akceptację inicjatora zawierającego adresy = {& v1, & v2 itd.})?:
std::array<T,N>
, łatwy do zdefiniowania w kodzie aplikacji, nie został dodany do std :: do C ++ 11, prawdopodobnie dlatego, że warto mieć to bez możliwości zrobić= {1,2,3};
Czy to „hacking językowy”?Komentarz do Twojej zmiany:
Lepszym rozwiązaniem jest
std::reference_wrapper
.Szczegóły: http://www.cplusplus.com/reference/functional/reference_wrapper/
Przykład:
źródło
reference_wrapper
.Tablica jest niejawnie konwertowana na wskaźnik, a wskaźnik do odwołania jest niedozwolony w C ++
źródło
Biorąc pod uwagę
int& arr[] = {a,b,c,8};
, co to jestsizeof(*arr)
?Wszędzie indziej odniesienie jest traktowane jako po prostu samo w sobie, więc
sizeof(*arr)
powinno po prostu byćsizeof(int)
. Ale to spowodowałoby, że arytmetyka wskaźnika tablicy na tej tablicy byłaby nieprawidłowa (zakładając, że odwołania nie mają takich samych szerokości, to ints). Aby wyeliminować niejednoznaczność, jest to zabronione.źródło
struct
którego jedynym członkiem jest ta referencja i dowiedz się ..?Ponieważ, jak wielu tutaj powiedziało, odniesienia nie są obiektami. są po prostu aliasami. To prawda, że niektóre kompilatory mogą zaimplementować je jako wskaźniki, ale standard tego nie wymusza / nie określa. A ponieważ odniesienia nie są obiektami, nie możesz ich wskazać. Przechowywanie elementów w tablicy oznacza, że istnieje pewien rodzaj adresu indeksu (tj. Wskazujący na elementy o określonym indeksie); i dlatego nie możesz mieć tablic odniesień, ponieważ nie możesz ich wskazać.
Zamiast tego użyj boost :: reference_wrapper lub boost :: tuple; lub po prostu wskazówki.
źródło
Uważam, że odpowiedź jest bardzo prosta i ma związek z semantycznymi regułami odwołań i sposobem obsługi tablic w C ++.
W skrócie: referencje można traktować jako struktury, które nie mają domyślnego konstruktora, więc obowiązują wszystkie te same reguły.
1) Semantycznie odwołania nie mają wartości domyślnej. Referencje można tworzyć tylko poprzez odwoływanie się do czegoś. Referencje nie mają wartości reprezentującej brak odniesienia.
2) Podczas przydzielania tablicy o rozmiarze X program tworzy kolekcję obiektów inicjalizowanych domyślnie. Ponieważ odwołanie nie ma wartości domyślnej, utworzenie takiej tablicy jest semantycznie niedozwolone.
Ta reguła dotyczy również struktur / klas, które nie mają domyślnego konstruktora. Poniższy przykład kodu nie kompiluje się:
źródło
Object
, po prostu trzeba się upewnić, aby je wszystkie zainicjować:Object objects[1] = {Object(42)};
. W międzyczasie nie możesz utworzyć tablicy odniesień, nawet jeśli zainicjujesz je wszystkie.Dzięki tej strukturze szablonu możesz podejść dość blisko. Musisz jednak zainicjować wyrażeniami, które są wskaźnikami do T, a nie T; i tak, chociaż możesz w podobny sposób łatwo utworzyć 'fake_constref_array', nie będziesz w stanie powiązać tego z wartościami r, jak to zrobiono w przykładzie OP ('8');
-> A = 0 B = 7 X = {0,8,0}
źródło
Obiekt referencyjny nie ma rozmiaru. Jeśli napiszesz
sizeof(referenceVariable)
, poda on rozmiar obiektu, do którego się odwołujeszreferenceVariable
, a nie samego odniesienia. Nie ma własnego rozmiaru, dlatego kompilator nie może obliczyć rozmiaru wymaganego przez tablicę.źródło
Kiedy przechowujesz coś w tablicy, jego rozmiar musi być znany (ponieważ indeksowanie tablicy zależy od rozmiaru). Wg standardu C ++ Nie określono, czy referencja wymaga przechowywania, czy też nie, ponieważ indeksowanie tablicy referencji nie byłoby możliwe.
źródło
struct
, magicznie wszystko staje się określone, dobrze zdefiniowane, gwarantowane i ma deterministyczny rozmiar. Dlaczego zatem istnieje ta pozornie całkowicie sztuczna różnica?struct
zapewnia opakowanie , zaczną nasuwać się dobre możliwe odpowiedzi - ale dla mnie nikt jeszcze tego nie zrobił, mimo że jest to jedno z bezpośrednich wyzwań dla wszystkich powszechnych uzasadnienia, dlaczego nie można używać nieopakowanych referencji.Wystarczy dodać do całej rozmowy. Ponieważ tablice wymagają kolejnych lokalizacji pamięci do przechowywania elementu, więc jeśli utworzymy tablicę referencji, nie ma gwarancji, że będą one znajdować się w kolejnych lokalizacjach pamięci, więc dostęp będzie problemem i dlatego nie możemy nawet zastosować wszystkich operacji matematycznych na szyk.
źródło
Rozważ tablicę wskaźników. Wskaźnik to tak naprawdę adres; więc kiedy inicjalizujesz tablicę, analogicznie mówisz komputerowi: „przydziel ten blok pamięci do przechowywania tych liczb X (które są adresami innych elementów)”. Następnie, jeśli zmienisz jeden ze wskaźników, zmieniasz tylko to, na co on wskazuje; nadal jest to adres numeryczny, który sam znajduje się w tym samym miejscu.
Odwołanie jest analogiczne do aliasu. Gdybyś miał zadeklarować tablicę odniesień, po prostu powiedziałbyś komputerowi: „przydziel tę amorficzną plamę pamięci składającą się z tych wszystkich różnych elementów rozrzuconych dookoła”.
źródło
struct
którego jedyny członek jest odniesieniem, prowadzi do czegoś całkowicie legalnego, przewidywalnego i nieamorficznego? Dlaczego użytkownik musi zawijać odniesienie, aby w magiczny sposób uzyskać moc obsługującą tablicę, czy też jest to tylko sztuczne ograniczenie? IMO żadna odpowiedź nie dotyczyła tego bezpośrednio. Zakładam, że obalenie brzmi: „Obiekt opakowujący zapewnia, że można użyć semantyki wartości w postaci obiektu opakowującego, dzięki czemu można mieć gwarancje wartości, które są potrzebne, ponieważ np. W jaki sposób można rozróżnić element i adres referencyjny” ... Czy to miałeś na myśli?Właściwie jest to mieszanka składni C i C ++.
Powinieneś albo używać czystych tablic C, które nie mogą być referencjami, ponieważ referencje są tylko częścią C ++. Lub pójdziesz drogą C ++ i użyjesz klasy
std::vector
lubstd::array
do swoich celów.Jeśli chodzi o część edytowaną: mimo że
struct
jest elementem z C, definiujesz konstruktora i funkcje operatora, co czyni go C ++class
. W związku zstruct
tym nie można kompilować w czystym C!źródło
std::vector
lub teżstd::array
nie może zawierać odniesień. Standard C ++ jest dość wyraźny, że żaden kontener nie może.