Dlaczego C ++ zabrania anonimowych struktur?

93

Niektóre kompilatory C ++ zezwalają na anonimowe związki i struktury jako rozszerzenie standardowego C ++. Jest to trochę cukru syntaktycznego, który czasami jest bardzo pomocny.

Jakie jest uzasadnienie, które uniemożliwia to, aby stało się to częścią normy? Czy istnieje techniczna blokada drogowa? Filozoficzny? Czy po prostu nie ma wystarczającej potrzeby uzasadnienia tego?

Oto próbka tego, o czym mówię:

struct vector3 {
  union {
    struct {
      float x;
      float y;
      float z;
    };
    float v[3];
  };
};

Mój kompilator to zaakceptuje, ale ostrzega, że „bezimienna struktura / związek” jest niestandardowym rozszerzeniem C ++ .

Adrian McCarthy
źródło
3
Najwyraźniej jest pewne zamieszanie co do tego, co masz na myśli. Czy możesz podać przykład kodu, który kompiluje się tylko z powodu rozszerzenia kompilatora?
Rob Kennedy,
75
Zauważ, że istnieją dwa koncepcje, które brzmią podobnie, ale są bardzo różne: struktury nienazwane i struktury anonimowe . Pierwszy to ten, który obsługuje C ++: struct { int i; } a; a.i = 0;(typ nie ma nazwy). Drugi to ten, którego C ++ nie obsługuje: struct { int i; }; i = 0;(typ nie ma nazwy i ucieka do otaczającego zakresu). C ++, jednak nie obsługiwać zarówno anonimowe i anonimowe związków .
Johannes Schaub - litb
Wygląda to na dość interesującą bibliotekę wektorów VMMLib. Uważam, że problem polega na tym, że związek zawiera nienazwaną strukturę, ale nie jestem pewien.
greyfade
1
FWIW Jest "anonimowy", a nie "nienazwany", a związki wspierane, jak mówi litb. stackoverflow.com/q/14248044/560648
Lightness Races in Orbit
1
@AdrianMcCarthy: W porządku (FSVO „w porządku”; nieznośny kompilator jest tajemniczy), ale właśnie „bez nazwy” jest niepowiązaną, standardową koncepcją.
Wyścigi lekkości na orbicie

Odpowiedzi:

50

Jak inni zauważyli, anonimowe związki są dozwolone w standardowym C ++, ale anonimowe struktury nie są.

Powodem tego jest to, że C obsługuje anonimowe związki, ale nie anonimowe struktury *, więc C ++ obsługuje te pierwsze ze względu na kompatybilność, ale nie obsługuje tych drugich, ponieważ nie jest to potrzebne do zapewnienia zgodności.

Ponadto anonimowe struktury w C ++ nie są zbyt przydatne. Zastosowanie wykazać, aby mieć struct zawierający trzy pływaki, które mogą być określone albo .v[i], albo .x, .yi .zwierzę, wyniki w niezdefiniowanej zachowań w C ++. C ++ nie pozwala na pisanie do jednego członka związku, powiedzmy .v[1], a następnie czytanie od innego członka, powiedzmy .y. Chociaż kod, który to robi, nie jest rzadkością, w rzeczywistości nie jest dobrze zdefiniowany.

Możliwości C ++ dla typów zdefiniowanych przez użytkownika zapewniają alternatywne rozwiązania. Na przykład:

struct vector3 {
  float v[3];
  float &operator[] (int i) { return v[i]; }
  float &x() { return v[0]; }
  float &y() { return v[1]; }
  float &z() { return v[2]; }
};

* C11 najwyraźniej dodaje anonimowe struktury, więc przyszła wersja C ++ może je dodać.

bames53
źródło
2
+1: Mój przykład opiera się na niezdefiniowanym zachowaniu w C ++ - czymś, o czym nie byłem świadomy, kiedy pisałem pytanie.
Adrian McCarthy
2
„C ++ nie pozwala na pisanie do jednego członka unii [...], a następnie czytanie od innego członka” - chyba że wspomniane elementy są obiektami o standardowym układzie i mają wspólną początkową sekwencję własnych członków, a ty ' ponownie piszą / czytają swoich członków we wspomnianej wspólnej sekwencji początkowej. To jest dozwolone (tj. Zdefiniowane).
underscore_d
5
@underscore_d: Tak, jeśli typy mają standardowy układ ze wspólną sekwencją początkową. Jednak struktura nigdy nie może aliasować tablicy w ten sposób, ponieważ zasady „wspólnej sekwencji początkowej” języka C ++ stanowią, że wspólna sekwencja początkowa może występować tylko między strukturami . Tablice nie są wymienione, więc nie mogą tak aliasów.
Nicol Bolas
@NicolBolas Och, haha ​​- uwierz mi - wiele razy marzyłem, aby w tym dodatku znalazły się tablice i inne prymitywy! Ale nie myślałem zbyt wiele o możliwych praktycznych ograniczeniach tego, więc prawdopodobnie nie jest to tak proste, jak się obecnie wydaje. Mój komentarz był bardziej ogólny, ale mogłem zaryzykować zasugerowanie przez pominięcie, że myślałem, że tablice zostały uwzględnione w tym, więc dziękuję za dodanie tego.
podkreślenie_d
„Powodem tego jest to, że C obsługuje anonimowe związki, ale nie anonimowe struktury” - Nie. Twój przypis wyjaśnia, że ​​mówiłeś tutaj o C99 lub wcześniej. Termin „anonimowy związek” nie pojawia się nigdzie w standardzie C99. GCC twierdzi w diagnostyce (z opcjami -std = c99 -pedantic), że „ISO C99 nie obsługuje nienazwanych struktur / związków”. Standard nie wspomina nic o nienazwanych członkach poza nienazwanymi polami bitowymi. Nie jestem do końca pewien, czy deklaracje struktur są deklaracjami, ale jeśli tak, to anonimowe związki są naruszeniem ograniczenia na 6,7p2, w najlepszym razie niezdefiniowane.
21

Powiem, że możesz wyczyścić swoją vector3deklarację, używając po prostu plikuunion

union vector3 {
  struct { float x, y, z; } ;
  float v[3] ;
} ;

Jasne, anonimowe struktury były rozszerzeniem MSVC . Ale ISO C11 pozwala na to teraz, a gcc na to pozwala , podobnie jak kompilator llvm firmy Apple.

Dlaczego w C11, a nie w C ++ 11? Nie jestem pewien, ale praktycznie większość (gcc ++, MSVC ++ i kompilator C ++ firmy Apple) obsługuje je.

bobobobo
źródło
1
+1 za aktualne informacje. Powodem, dla którego miałem zewnętrzną strukturę, było to, że „prawdziwy kod” również zawierał metody.
Adrian McCarthy
Jedynymi rzeczami, których nie można zrobić z unią, są statyczne elementy składowe danych lub dziedziczenie .
bobobobo
2
Dzięki. Nigdy nie można było używać nowej unii jako struktury lub klasy.
Adrian McCarthy,
Wiem, że studio Sun domyślnie nie obsługiwało struktury anonimowej przed C ++ 11. Jeśli piszesz kod dla wielu platform, a kompilatory nie są uaktualniane do C + 11, nie używaj anonimowej struktury.
irys
6

Nie wiem co masz na myśli. Sekcja 9.5 specyfikacji C ++, klauzula 2:

Połączenie formy

union { member-specification } ;

nazywany jest związkiem anonimowym; definiuje nienazwany obiekt nienazwanego typu.

Możesz też robić takie rzeczy:

void foo()
{
  typedef
  struct { // unnamed, is that what you mean by anonymous?
    int a;
    char b;
  } MyStructType; // this is more of a "C" style, but valid C++ nonetheless

  struct { // an anonymous struct, not even typedef'd
    double x;
    double y;
  } point = { 1.0, 3.4 };
}

Nie zawsze bardzo przydatne ... chociaż czasami przydatne w nieprzyjemnych definicjach makr.

Dan
źródło
11
-1, ponieważ mówi, że definiuje anonimową strukturę. Zobacz komentarze powyżej do pytania - definiujesz strukturę nienazwaną, a nie anonimową.
Johannes Schaub - litb
1

Związki mogą być anonimowe; patrz Norma, 9.5 paragraf 2.

Jaki cel Twoim zdaniem spełnia anonimowa struktura lub klasa? Zanim zacznę spekulować, dlaczego czegoś nie ma w standardzie, chciałbym mieć pojęcie, dlaczego tak powinno być, i nie widzę zastosowania dla struktury anonimowej.

David Thornley
źródło
1

Opierając się na edycji, komentarzach i artykule MSDN: Struktury anonimowe , zaryzykuję przypuszczenie - słabo pasuje do koncepcji hermetyzacji. Nie spodziewałbym się, że członek klasy będzie bałaganił z moją przestrzenią nazw klasy poza zwykłym dodaniem jednego członka. Ponadto zmiany w strukturze anonimowej mogą wpływać na moją klasę bez pozwolenia.

JonM
źródło
1
Ze względu na sposób, w jaki tworzone są anonimowe struktury / unions (jest to specjalna, wbudowana składnia, której nie można ukryć inaczej niż za pomocą makra), nie można być zaskoczonym, że jakiś członek, którego używasz, jest członkiem anonimowym. Więc nie sądzę, żeby to rozumowanie miało jakikolwiek sens. Rzeczywistym powodem jest to, że anonimowe związki obsługiwane w C ++, tylko w celu zapewnienia zgodności z C. C nie obsługuje struktur anonimowych (do C11), więc C ++ też nie.
bames53
1

Twój kod

union {
  struct {
    float x;
    float y;
    float z;
  };
  float v[3];
};

jest jak

union Foo {
   int;
   float v[3];
};

który z pewnością jest nieważny (w C99 i wcześniej).

Powodem jest prawdopodobnie uproszczenie parsowania (w C), ponieważ w takim przypadku wystarczy sprawdzić, czy treść struktury / unii ma tylko „deklaratory deklarujące”, takie jak

Type field;

To powiedziawszy, gcc i „inne kompilatory” obsługują nienazwane pola jako rozszerzenie.

Edycja: struktury anonimowe są teraz oficjalnie obsługiwane w C11 (§6.7.2.1 / 13).

kennytm
źródło
5
Z punktu widzenia analizy nie sądzę, żeby union { ... }to było inne niż struct { ... }. To pierwsze jest ważne, ale drugie nie.
Johannes Schaub - litb
3
Biorąc pod uwagę, jak absurdalnie trudna jest analiza C ++ w ogóle, wątpię, aby standard wprowadził niedozwolone nienazwane struktury i związki tylko po to, aby uprościć parsowanie.
Adrian McCarthy
@Adrian: Powiedziałem C, nie C ++. C ++ przyjmuje składnię języka C i rozszerza ją. Prawdopodobnie twórcy C ++ nie widzą potrzeby zezwalania na nienazwanych członków struktury / unii, więc nie mieszają się w tę część składni.
kennytm
@Adrian, dobra uwaga Adrian, zawsze nie sądziłem, że „zbyt trudne do wdrożenia” mogłoby kiedykolwiek być zmartwieniem Bjarne'a i załogi
bobobobo
C i C ++ obsługują nienazwane związki, więc komentarz, który union { ... };jest nieprawidłowy, nie jest poprawny.
bames53