49 votes

Pourquoi la définition de la "disposition standard" de la POD du C++11 est-elle telle qu'elle est ?

Je me penche sur la nouvelle définition assouplie de la POD en C++11 (section 9.7)

Une classe de mise en page standard est une classe qui :

  • n'a pas de membres de données non statiques de type classe de présentation non standard (ou tableau de tels types) ou référence,
  • n'a pas de fonctions virtuelles (10.3) ni de classes de base virtuelles (10.1),
  • a le même contrôle d'accès (article 11) pour tous les membres de données non statiques,
  • n'a pas de classes de base de mise en page non standard,
  • soit n'a pas de membres de données non statiques dans la classe la plus dérivée et au plus une classe de base avec des membres de données non statiques ou n'a pas de classes de base avec des membres de données non statiques, et
  • n'a pas de classes de base du type même type que le premier élément de données non statique .

J'ai souligné les éléments qui m'ont surpris.

Que se passerait-il si nous tolérions que les membres des données soient soumis à des contrôles d'accès variables ?

Que se passerait-il si le premier membre des données était également une classe de base ?

struct Foo {};
struct Good : Foo {int x; Foo y;};
struct Bad  : Foo {Foo y; int x;};

Je reconnais qu'il s'agit d'une construction bizarre, mais pourquoi devrait-on Bad être interdites, mais pas Good ?

Enfin, que se passerait-il si plus d'une classe constitutive avait des membres de données ?

0 votes

struct a toujours eu tous ses membres public . C++11 est maintenant privé ?

0 votes

@Mu : Oui, par défaut, les membres d'une structure sont publics. Les membres d'une classe sont privés par défaut, inversement.

15 votes

@Code Il a toujours été possible d'avoir private dans un struct en C++ (mais pas en C). La valeur par défaut est public Cependant, il ne s'agit pas d'une simple question d'argent, mais bien d'une question de sécurité.

28voto

Johannes Schaub - litb Points 256113

Vous êtes autorisé à convertir l'adresse d'un objet de la classe de présentation standard en un pointeur sur son premier membre et à revenir par l'un des paragraphes suivants, ce qui est également souvent le cas en C :

struct A { int x; };
A a;

// "px" is guaranteed to point to a.x
int *px = (int*) &a;

// guaranteed to point to a
A *pa = (A*)px; 

Pour que cela fonctionne, le premier membre et l'objet complet doivent avoir la même adresse (le compilateur ne peut pas ajuster le pointeur int d'un octet parce qu'il ne peut pas savoir s'il s'agit d'un membre d'un objet de type A ou non).

Enfin, que se passerait-il si plus d'une classe constitutive avait des membres de données ?

Au sein d'une classe, les membres sont attribués par adresses croissantes selon l'ordre de déclaration. Cependant, le C++ ne dicte pas l'ordre d'allocation des membres de données entre les classes. Si la classe dérivée et la classe de base ont toutes deux des membres de données, la norme ne définit pas d'ordre pour leurs adresses, afin de donner à l'implémentation une flexibilité totale dans l'agencement de la mémoire. Mais pour que la distribution ci-dessus fonctionne, il faut savoir quel est le "premier" membre dans l'ordre d'allocation !

Que se passerait-il si le premier membre des données était également une classe de base ?

Si la classe de base a le même type que le premier membre de données, les implémentations qui placent les classes de base avant les objets de la classe dérivée en mémoire devraient avoir un octet de remplissage avant les membres de données de l'objet de la classe dérivée en mémoire (la classe de base aurait une taille de un), pour éviter d'avoir la même adresse pour la classe de base et le premier membre de données (en C++, deux objets distincts du même type ont toujours des adresses différentes). Mais cela rendrait à nouveau impossible le moulage de l'adresse de l'objet de la classe dérivée vers le type de son premier membre de données.

0 votes

C'est une excellente réponse, sauf que je ne suis pas sûr du dernier paragraphe - est-ce que ce rembourrage est une classe de base qui n'a pas de membres de données ? Je comprends la mécanique du cast-to-member mais je n'accepte pas que cela nuise à la "mise en page standard" d'assouplir cela parce que nous n'avons besoin de supporter cette vieille astuce que pour le agrégats

0 votes

@spraff la spécification le permet pour les classes de mise en page standard, pas seulement pour les agrégats. Une classe de base ne comportant que des fonctions et aucun membre de données peut être créée par des traits ou des cas d'utilisation SFINAE. Dans de tels cas, il serait dommage de nuire à la garantie qu'il n'y a pas de padding avant le premier membre de données, je pense.

0 votes

Pourquoi ne pas assouplir l'obligation d'avoir des objets distincts et des pointeurs distincts pour les classes de base ne contenant pas de données par rapport à la même classe en tant que membre ? Cela entraînerait-il des effets secondaires désagréables ? (AFAIK dans le cadre du problème du diamant, une classe de base vide dupliquée aurait déjà une valeur de pointeur pour les deux "instances" en raison de la garantie d'absence de remplissage).

26voto

Steve Jessop Points 166970

Il s'agit essentiellement d'une question de compatibilité avec C++03 et C :

  • même contrôle d'accès - Les implémentations C++03 sont autorisées à utiliser les spécificateurs de contrôle d'accès pour réorganiser les (groupes de) membres d'une classe, par exemple pour mieux l'empaqueter.
  • plus d'une classe dans la hiérarchie avec des membres de données non statiques - C++03 ne dit pas où les classes de base sont situées, ou si le remplissage est éludé dans les sous-objets de la classe de base qui seraient présents dans un objet complet du même type.
  • classe de base et premier membre du même type - en raison de la deuxième règle, si le type de la classe de base est utilisé pour un membre de données, il est alors doit soit une classe vide. De nombreux compilateurs mettent en œuvre l'optimisation de la classe de base vide, de sorte que ce que dit Andreas sur le fait que les sous-objets ont la même adresse serait vrai. Je ne suis pas sûr de ce qui, dans les classes à structure standard, fait qu'il est mauvais pour le sous-objet de la classe de base d'avoir la même adresse qu'un premier membre de données du même type, mais que cela n'a pas d'importance lorsque le sous-objet de la classe de base a la même adresse qu'un premier membre de données d'un type différent. [C'est parce que différents objets du même type ont des adresses différentes, même s'il s'agit de sous-objets vides. Merci à Johannes]

C++0x probablement pourrait a défini que ces éléments sont également des types de mise en page standard, auquel cas il définit également la manière dont ils sont présentés, dans la même mesure que pour les types de mise en page standard. La réponse de Johannes va plus loin, regardez son exemple d'une belle propriété des classes de présentation standard avec laquelle ces choses interfèrent.

Mais si c'était le cas, certaines implémentations seraient obligées de modifier la disposition des classes pour répondre aux nouvelles exigences, ce qui est gênant pour la compatibilité des structures entre les différentes versions de ce compilateur avant et après C++0x. Cela rompt l'ABI du C++, fondamentalement.

D'après ce que j'ai compris de la définition de la mise en page standard, on a examiné quelles exigences de la POD pouvaient être assouplies sans compromettre les mises en œuvre existantes. Je suppose donc, sans vérifier, que les exemples ci-dessus sont des exemples où une implémentation existante de C++03 hace utiliser la nature non POD de la classe pour faire quelque chose d'incompatible avec la mise en page standard.

0 votes

Au début, je l'acceptais, mais plus j'y réfléchis, plus il semble que la façon dont le compilateur ordonne/aligne les membres d'une classe/structure n'a pas d'importance - seule la base et les structures membres sont conservées. contenu à l'intérieur d'eux-mêmes au fur et à mesure que la classe dont ils sont membres/bases est définie par le compilateur. Rien de ce qui était La POD cesserait de l'être si nous autorisions des contrôles d'accès hétérogènes et des bases d'appartenance multiples !

0 votes

Il est intéressant de noter que C++11 a clarifié "same access control"/"reorder by access control specifier" pour signifier uniquement lorsque les spécificateurs sont différents - au cas où, pour une raison quelconque, vous voudriez inclure plusieurs spécificateurs identiques redondants - alors que C++03 autorisait le réordonnancement autour de ces spécificateurs. Je ne sais pas si un compilateur a réellement fait cela.

0 votes

@Steve Jessop "so what Andreas says about the sub-objects having the" Je pense que vous vouliez dire "member sub-object".

8voto

Bo Persson Points 42821

Que se passerait-il si nous tolérions que les membres des données soient soumis à des contrôles d'accès variables ?

Le langage actuel stipule que le compilateur ne peut pas réorganiser les membres soumis au même contrôle d'accès. Par exemple :

struct x
{
public:
    int x;
    int y;
private:
    int z;
};

Ici, x doit être alloué avant y, mais il n'y a aucune restriction sur z par rapport à x et y.

struct y
{
public:
    int x;
public:
    int y;
};

La nouvelle formulation prévoit que y est toujours un POD malgré les deux public s. Il s'agit en fait d'un assouplissement des règles.

4voto

Andreas Brinck Points 23806

Pour quelle raison Bad n'est pas autorisé, permettez-moi de citer un article que j'ai trouvé :

Cela garantit que deux sous-objets qui ont le même s appartenant au même objet le plus dérivé ne sont pas alloués à la même adresse. même adresse.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2172.html

1 votes

Si sizeof(Foo) est non nulle alors struct Bar:Foo{Foo f;}; sera composé de deux éléments distincts Foo à deux endroits distincts. C'est un lien intéressant, mais je ne vois pas comment ces deux Foo pourraient avoir la même adresse.

1 votes

Il convient de noter que s'il existe un premier membre de données, la base est nécessairement hace ont une taille nulle (selon le 5e point, si elles ont des membres de données non statiques, aucune classe de base ne peut les avoir et donc toutes les classes de base ont une taille nulle).

3 votes

@spraff : cela ne suit pas. sizeof(Foo) est non nulle pour toute classe Foo Mais si la classe est vide, même si sa taille n'est pas nulle, elle ne peut occuper aucun espace lorsqu'elle est utilisée comme sous-objet.

2voto

Nicolas Grebille Points 872

D'après le point 5, il semble que les deux ne soient pas compatibles puisque la classe la plus dérivée a un membre de données non statique (l'int), elle ne peut pas avoir une classe de base avec un membre de données non statique.

Je l'entends comme suit : "seule une classe "de base" (c'est-à-dire la classe elle-même ou l'une des classes dont elle hérite) peut avoir des membres de données non statiques".

0 votes

"un seul de la 'base'..." -- oui, mais pourquoi ?

3 votes

D'après le document cité ci-dessus, il semble que ce soit parce que la norme n'impose aucune restriction quant à l'endroit où les données de la classe de base sont attribuées "en relation avec les données de la classe dérivée", c'est-à-dire que l'ordre de présentation (données de base et données dérivées) n'est pas spécifié. Par conséquent, cela romprait la garantie de compatibilité de la disposition des types de pods (si j'ai bien compris).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X