Je joue autour avec [[no_unique_address]]
dans c++20
.
Dans l'exemple sur cppreference, nous avons un type Empty
et un type videsZ
struct Empty {}; // empty class
struct Z {
char c;
[[no_unique_address]] Empty e1, e2;
};
Apparemment, la taille de Z
doit être au moins 2
parce que les types de e1
et e2
sont les mêmes.
Cependant, je veux vraiment avoir Z
avec la taille 1
. Cela m'a fait réfléchir, qu'en est-il Empty
de l'encapsulation dans une classe wrapper avec un paramètre de modèle supplémentaire qui applique différents types de e1
et 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;
};
Malheureusement sizeof(Z1)==2
. Y a-t-il une astuce pour que la taille Z1
soit de un?
Je teste cela avec gcc version 9.2.1
etclang version 9.0.0
Dans ma demande, j'ai beaucoup de types vides du formulaire
template <typename T, typename S>
struct Empty{
[[no_unique_address]] T t;
[[no_unique_address]] S s;
};
Qui est un type vide si T
et S
sont également des types vides et distincts! Je veux que ce type à vide , même si T
et S
sont les mêmes types.
T
lui-même? Cela générerait des types distincts. En ce moment, le fait que les deuxWrapper
héritent deT
vous retient ...T
? À l'heure actuelle,T
est un argument de modèle.T
.Réponses:
Vous ne pouvez pas comprendre cela. Techniquement parlant, vous ne pouvez même pas garantir qu'il sera vide même si
T
etS
sont différents types vides. Rappelez-vous:no_unique_address
est un attribut; sa capacité à masquer des objets dépend entièrement de l' implémentation. Du point de vue des normes, vous ne pouvez pas appliquer la taille des objets vides.À mesure que les implémentations C ++ 20 arrivent à maturité, vous devez supposer qu'elles
[[no_unique_address]]
suivront généralement les règles d'optimisation de la base vide. À savoir, tant que deux objets du même type ne sont pas des sous-objets, vous pouvez probablement vous attendre à vous cacher. Mais à ce stade, c'est une sorte de chance.En ce qui concerne le cas spécifique
T
etS
le même type, ce n'est tout simplement pas possible. Malgré les implications du nom "no_unique_address", la réalité est que C ++ exige que, étant donné deux pointeurs vers des objets du même type, ces pointeurs pointent vers le même objet ou ont des adresses différentes. J'appelle cela la "règle d'identité unique", etno_unique_address
cela n'affecte pas cela. Depuis [intro.object] / 9 :Les membres de types vides déclarés comme
[[no_unique_address]]
étant de taille nulle, mais avoir le même type rendent cela impossible.En effet, y penser, tenter de masquer le type vide via l'imbrication viole toujours la règle d'identité unique. Considérez votre
Wrapper
et votreZ1
cas. Étant donné unz1
qui est une instance deZ1
, il est clair quez1.e1
etz1.e2
sont des objets différents avec des types différents. Cependant,z1.e1
n'est pas imbriqué à l'intérieurz1.e2
ni vice-versa. Et alors qu'ils ont des types différents,(Empty&)z1.e1
et ne(Empty&)z1.e2
sont pas des types différents. Mais ils pointent vers des objets différents.Et selon la règle d'identité unique, ils doivent avoir des adresses différentes. Ainsi , même si
e1
ete2
sont théoriquement différents types, leurs internes doivent aussi Obey identité unique par rapport aux autres sous - objets dans le même objet contenant. Récursivement.Ce que vous voulez est tout simplement impossible en C ++ tel qu'il est actuellement, quelle que soit la façon dont vous essayez.
la source
Pour autant que je sache, ce n'est pas possible si vous voulez avoir les deux membres. Mais vous pouvez vous spécialiser et n'avoir qu'un seul membre lorsque le type est identique et vide:
Bien sûr, le reste du programme qui utilise les membres devrait être modifié pour traiter le cas où il n'y a qu'un seul membre. Peu importe le membre utilisé dans ce cas - après tout, c'est un objet sans état sans adresse unique. Les fonctions membres affichées devraient simplifier les choses.
Vous pouvez introduire plus de spécialisations pour prendre en charge la compression récursive des paires vides:
Encore plus, pour compresser quelque chose comme
Empty<Empty<A, char>, A>
.la source
sizeof(Empty<Empty<A,A>,A>{})==2
où seA
trouve une structure complètement vide.get_empty<T>
fonction. Ensuite, vous pouvez réutiliser leget_empty<T>
à gauche ou à droite si cela fonctionne déjà là-bas.