2014-08-28 13 views
16

Jak postąpisz w ten sposób w standardowym C++ 11/14? Ponieważ, jeśli się nie mylę, nie jest to standardowy kod zgodny z anonimowymi strukturami.Anonimowy związek i struktura

Chciałbym uzyskać dostęp do członków w taki sam sposób, jak z tym.

template <typename some_type> 
struct vec 
{ 
    union { 
     struct { some_type x, y, z; }; 
     struct { some_type r, g, b; }; 

     some_type elements[3]; 
    }; 
}; 
+0

Jeśli nalegasz na możliwość napisania 'vec t; t.x = 10; 'następnie możesz nadać elementom referencyjnym' x', 'y',' z' itd. i zainicjować je tak, aby odnosiły się do odpowiedniego elementu 'elements', ale zwiększy to znacznie rozmiar struktury. Jeśli możesz tolerować 't.x() = 10', a następnie uczynić je funkcjami składowymi. –

+1

Ale myślę, że 'vec.x = 42; assert (vec.r == 42); 'i tak nie jest gwarantowane przez standard (ale może to być kompilator). – Jarod42

+0

@ Jarod42 N3936 [class.mem]/18: "Jeśli ujednolicony układ zawiera dwa lub więcej struktur o standardowym układzie, które mają wspólną początkową sekwencję, i jeśli standardowy obiekt złożenia zawiera obecnie jeden z tych standardów: układ struktur, jest dozwolone do sprawdzenia wspólnej początkowej części któregokolwiek z nich. ... " – Casey

Odpowiedz

12

Tak, ani C++ 11, ani C++ 14 nie umożliwiają anonimowych struktur. This answer zawiera pewne uzasadnienie, dlaczego tak się dzieje. Musisz nazwać struktury, a także nie można ich zdefiniować w anonimowym związku.

§9.5/5 [class.union]

...element specyfikacją anonimowego Unia określa nie tylko statycznych elementów danych. [Uwaga: Zagnieżdżone typy, anonimowe związki i funkcje nie mogą być zadeklarowane w anonimowym związku. -end note]

Przenosi definicje struktury poza związek.

template <typename some_type> 
struct vec 
{ 
    struct xyz { some_type x, y, z; }; 
    struct rgb { some_type r, g, b; }; 

    union { 
     xyz a; 
     rgb b; 
     some_type elements[3]; 
    }; 
}; 

Teraz możemy wymagać some_type być standardowym układ ponieważ sprawia, że ​​wszystkich członków anonimowego unia układzie zgodnym. Here are the requirements dla standardowego typu układu. Są one opisane w rozdziale §9/7 normy.

Następnie z §9.2 [class.mem]

  dwóch standardowych układ struktura (Rozdział 9) typy układ kompatybilne, jeśli mają tę samą liczbę niestatyczny elementy danych i odpowiednie niestatyczne elementy danych (w kolejności deklaracji) mają typy kompatybilne z układem (3.9).
  Jeśli ujednolicony układ zawiera dwa lub więcej układów o standardowym układzie, które mają wspólną początkową sekwencję, a także jeśli standardowy obiekt złożony zawiera obecnie jeden z tych standardowych elementów, można przeglądać wspólną pierwszą część którejkolwiek z nich. Dwie struktury o standardowym układzie mają wspólną początkową sekwencję, jeśli odpowiadający element ma typ zgodny z układem i żaden z elementów nie jest polem bitowym lub oba są polami bitowymi o tej samej szerokości dla sekwencji jednego lub większej liczby początkowych elementów.

I dla elementu macierzy, od §3.9/9 [podstawowy.rodzaje]

... skalarne, typy klasy standardowej układ (punkt 9), układy tego typu i wersji cv wykwalifikowany tych typów (3.9.3) są zbiorczo nazywane typu standardowego układu .

Aby zapewnić some_type jest standardowy układ, należy dodać następujące ramach definicji vec

static_assert(std::is_standard_layout<some_type>::value, "not standard layout"); 

std::is_standard_layout jest zdefiniowany w nagłówku type_traits. Teraz wszyscy 3 członkowie waszego związku są układem standardowym, dwie struktury i tablica są kompatybilne z układem, więc 3 członkowie związku mają wspólną początkową sekwencję, która pozwala pisać, a następnie kontrolować (czytać) dowolne elementy należące do wspólnej początkowa sekwencja (cała rzecz w twoim przypadku).

+1

Tablica nie jest standardem struktura układu. –

+0

@ T.C. Właśnie zdałem sobie z tego sprawę, ponieważ ponownie czytałem po opublikowaniu (powinno to już wcześniej zrobić). Próbując znaleźć coś, co mówi, że jest, wydaje się głupie, jeśli typ jest układem standardowym, tablica nie jest gwarantowana. – Praetorian

+0

@Praetorian ma standardową gwarancję, że 'elementy [0]' będzie taka sama jak 'a.x' (i' b.r')? Nie mogę go znaleźć. –

7

Anonimowe związki są dozwolone w C++ 11/14. Patrz przykład ich wykorzystania w Bjarne Stroustrup's C++11 FAQ

chodzi anonimowych kodowanym zobaczyć Why does C++11 not support anonymous structs, while C11 does? i Why does C++ disallow anonymous structs and unions?

Choć większość kompilatory wsparcie anonimowe konstrukcjom, jeśli chcesz, aby Twój kod być zgodne ze standardem trzeba napisać coś takiego:

template <typename some_type> 
struct vec 
{ 
    union { 
     struct { some_type x, y, z; } s1; 
     struct { some_type r, g, b; } s2; 

     some_type elements[3]; 
    }; 
}; 
5

myślę pozostałe odpowiedzi jakby brakowało punkt pytanie:

życzę, aby uzyskać dostęp członków w taki sam sposób jak w przypadku tego produktu.

Innymi słowy, pytanie jest naprawdę „jak mogę określić typ vec w standardowym zgodny taki sposób, że dany obiekt u tego typu, u.x, u.r i u.elements[0] wszystkie odnoszą się do tego samego ? "

Cóż, jeśli nalegasz na tę składnię ... wtedy oczywista odpowiedź brzmi: referencje.

Więc:

template <typename some_type> 
struct vec 
{ 
    vec() = default; 
    vec(const vec& other) : elements{ other.elements[0], other.elements[1], other.elements[2] } {} 

    vec & operator=(const vec &other) { 
     elements[0] = other.elements[0]; 
     elements[1] = other.elements[1]; 
     elements[2] = other.elements[2]; 
     return *this; 
    } 

    some_type elements[3]; 
    some_type &x = elements[0], &y = elements[1], &z = elements[2]; 
    some_type &r = elements[0], &g = elements[1], &b = elements[2];  
}; 

Pierwszym problemem z tego podejścia jest to, że potrzebne są dodatkowe miejsca dla 6 członków odniesienia - co jest dość drogie jak na taki mały struktury.

Drugi problem z tego podejścia jest to, że biorąc pod uwagę const vec<double> v;, v.x jest nadal typu double &, więc można napisać v.x = 20; i mieć go skompilować bez ostrzeżenia lub błędu - tylko dostać niezdefiniowane zachowanie. Dość źle.

Tak, alternatywnie, można rozważyć użycie funkcje dostępowe:

template <typename some_type> 
struct vec 
{ 
    some_type elements[3]; 
    some_type &x() { return elements[0]; } 
    const some_type &x() const { return elements[0]; } 

    some_type &y() { return elements[1]; } 
    const some_type &y() const { return elements[1]; } 

    some_type &z() { return elements[2]; } 
    const some_type &z() const { return elements[2]; } 

    some_type &r() { return elements[0]; } 
    const some_type &r() const { return elements[0]; } 

    some_type &g() { return elements[1]; } 
    const some_type &g() const { return elements[1]; } 

    some_type &b() { return elements[2]; } 
    const some_type &b() const { return elements[2]; } 
}; 

Trzeba by napisać u.x() itd zamiast u.x, ale oszczędności przestrzeni jest znaczna, można również liczyć na kompilatora Generowane funkcje specjalne, to trywialnie kopiowalna, jeśli jest to some_type (co umożliwia pewne optymalizacje), jest agregatem i może używać składni inicjującej agregację, a także jest poprawna.

Demo. Zauważ, że sizeof(vec<double>) to 72 dla pierwszej wersji i tylko 24 dla drugiej.

+0

Jest to również najbezpieczniejsze podejście, ponieważ w przeciwieństwie do zjednoczenia gwarantuje to, że 'x() == elementy [0]' –