Mam zabawy z [[no_unique_address]]
w c++20
.
W przykładzie na cppreference mamy pusty typ Empty
i typZ
struct Empty {}; // empty class
struct Z {
char c;
[[no_unique_address]] Empty e1, e2;
};
Najwyraźniej rozmiar Z
musi być co najmniej 2
dlatego, że typy e1
i e2
są takie same.
Jednak naprawdę chcę mieć Z
rozmiar 1
. To dało mi do myślenia, co z zawijaniem Empty
w jakiejś klasie opakowania z dodatkowym parametrem szablonu, który wymusza różne typy e1
i e2
.
template <typename T, int i>
struct Wrapper : public T{};
struct Z1 {
char c;
[[no_unique_address]] Wrapper<Empty,1> e1;
[[no_unique_address]] Wrapper<Empty,2> e2;
};
Niestety sizeof(Z1)==2
. Czy jest jakiś sposób, aby zrobić z niego rozmiar Z1
?
Testuję to za pomocą gcc version 9.2.1
iclang version 9.0.0
W mojej aplikacji mam wiele pustych typów formularza
template <typename T, typename S>
struct Empty{
[[no_unique_address]] T t;
[[no_unique_address]] S s;
};
Który jest typem pustym, jeśli T
i S
są również typami pustymi i odrębnymi! Chcę tego typu mają być puste, nawet jeśli T
i S
to te same typy.
T
siebie? To generowałoby różne typy. W tej chwili fakt, że obajWrapper
dziedziczymy,T
powstrzymuje cię ...T
? W tej chwiliT
jest argumentem szablonu.T
.Odpowiedzi:
Nie możesz tego zdobyć. Technicznie rzecz biorąc, nie można nawet zagwarantować, że będzie on pusty, nawet jeśli
T
iS
są różne rodzaje pusty. Pamiętaj:no_unique_address
jest atrybutem; zdolność do ukrywania obiektów jest całkowicie zależna od implementacji. Ze standardowego punktu widzenia nie można narzucać wielkości pustych obiektów.W miarę dojrzewania implementacji C ++ 20 należy założyć, że
[[no_unique_address]]
będą one generalnie przestrzegać zasad optymalizacji pustej bazy. Mianowicie, dopóki dwa obiekty tego samego typu nie są podobiektami, prawdopodobnie można się spodziewać ukrycia. Ale w tym momencie to trochę szczęścia.Jeśli chodzi o konkretny przypadek
T
iS
bycie tego samego typu, jest to po prostu niemożliwe. Pomimo implikacji nazwy „no_unique_address”, w rzeczywistości C ++ wymaga, aby biorąc pod uwagę dwa wskaźniki dla obiektów tego samego typu, wskaźniki te wskazywały na ten sam obiekt lub miały różne adresy. Nazywam to „unikalną zasadą tożsamości” ino_unique_address
nie ma na to wpływu. Z [intro.object] / 9 :Członkowie pustych typów zadeklarowanych jako
[[no_unique_address]]
mają rozmiar zero, ale posiadanie tego samego typu uniemożliwia to.Rzeczywiście, myślenie o tym, próba ukrycia pustego typu poprzez zagnieżdżanie nadal narusza unikalną zasadę tożsamości. Rozważ swoją
Wrapper
i swojąZ1
sprawę. Biorąc pod uwagęz1
, który jest instancjąZ1
, oczywiste jest, żez1.e1
iz1.e2
różne przedmioty z różnych typów. Niez1.e1
jest jednak zagnieżdżonyz1.e2
ani odwrotnie. I chociaż mają różne typy(Empty&)z1.e1
i nie(Empty&)z1.e2
są różnymi typami. Ale wskazują na różne przedmioty.Zgodnie z unikalną regułą tożsamości muszą mieć różne adresy. Więc chociaż
e1
ie2
są nominalnie różne typy, ich wewnętrzne muszą również przestrzegać następujących zaleceń unikalna tożsamość wobec innych podobiektów w tym samym obiekcie zawierającym. Rekurencyjnie.To, czego chcesz, jest po prostu niemożliwe w C ++ w obecnej postaci, niezależnie od tego, jak spróbujesz.
źródło
O ile mi wiadomo, nie jest to możliwe, jeśli chcesz mieć obu członków. Ale możesz się specjalizować i mieć tylko jednego członka, gdy typ jest taki sam i pusty:
Oczywiście reszta programu, która korzysta z członków, musiałaby zostać zmieniona, aby zająć się przypadkiem, w którym jest tylko jeden członek. Nie powinno mieć znaczenia, który element zostanie użyty w tym przypadku - w końcu jest to obiekt bezstanowy bez unikalnego adresu. Pokazane funkcje składowe powinny to ułatwić.
Możesz wprowadzić więcej specjalizacji w celu obsługi rekurencyjnej kompresji pustych par:
Co więcej, aby kompresować coś takiego
Empty<Empty<A, char>, A>
.źródło
sizeof(Empty<Empty<A,A>,A>{})==2
gdzieA
jest całkowicie pusta struktura.get_empty<T>
funkcję. Następnie możesz ponownie użyćget_empty<T>
lewej lub prawej, jeśli już tam działa.