Question Quels sont les agrégats et les POD et comment / pourquoi sont-ils spéciaux?


Ce FAQ concerne les agrégats et les POD et couvre les matériaux suivants:

  • Quels sont Agrégats?
  • Quels sont COSSEs (Plain Old Data)?
  • Comment sont-ils liés?
  • Comment et pourquoi sont-ils spéciaux?
  • Quels changements pour C ++ 11?

445
2017-11-14 15:35


origine


Réponses:


Comment lire:

Cet article est plutôt long. Si vous voulez en savoir plus sur les agrégats et les POD (Plain Old Data), prenez le temps de le lire. Si vous êtes intéressé par les agrégats, lisez seulement la première partie. Si vous êtes uniquement intéressé par les POD, vous devez d'abord lire la définition, les implications et les exemples d'agrégats, puis vous mai passer aux POD mais je recommande quand même de lire la première partie dans son intégralité. La notion d'agrégat est essentielle pour la définition des POD. Si vous trouvez des erreurs (même mineures, y compris la grammaire, la stylistique, le formatage, la syntaxe, etc.) s'il vous plaît laissez un commentaire, je vais modifier.

Quels sont les agrégats et pourquoi ils sont spéciaux

Définition formelle de la norme C ++ (C ++ 03 8.5.1 §1):

Un agrégat est un tableau ou une classe (clause 9) sans déclaration par l'utilisateur   constructeurs (12.1), aucun membre de données privé ou protégé non statique (clause 11),   pas de classes de base (clause 10) et pas de fonctions virtuelles (10.3).

Alors, OK, analysons cette définition. Tout d'abord, tout tableau est un agrégat. Une classe peut aussi être un agrégat si ... wait! Rien n'est dit sur les structures ou les syndicats, ne peuvent-ils pas être des agrégats? Oui, ils peuvent. En C ++, le terme class fait référence à toutes les classes, structures et unions. Ainsi, une classe (ou struct, ou union) est un agrégat si et seulement si elle satisfait aux critères des définitions ci-dessus. Qu'est-ce que ces critères impliquent?

  • Cela ne signifie pas qu'une classe agrégée ne peut pas avoir de constructeur, en fait elle peut avoir un constructeur par défaut et / ou un constructeur de copie tant qu'ils sont déclarés implicitement par le compilateur, et non explicitement par l'utilisateur

  • Pas privé ou protégé membres de données non statiques. Vous pouvez avoir autant de fonctions membres privées et protégées (mais pas de constructeurs) ainsi que autant de fonctions privées ou protégées. statique les membres de données et les fonctions membres comme vous le souhaitez et ne violent pas les règles pour les classes agrégées

  • Une classe d'agrégat peut avoir un opérateur et / ou un destructeur d'affectation de copie déclaré par l'utilisateur / défini par l'utilisateur

  • Un tableau est un agrégat même s'il s'agit d'un tableau de type de classe non agrégé.

Voyons maintenant quelques exemples:

class NotAggregate1
{
  virtual void f() {} //remember? no virtual functions
};

class NotAggregate2
{
  int x; //x is private by default and non-static 
};

class NotAggregate3
{
public:
  NotAggregate3(int) {} //oops, user-defined constructor
};

class Aggregate1
{
public:
  NotAggregate1 member1;   //ok, public member
  Aggregate1& operator=(Aggregate1 const & rhs) {/* */} //ok, copy-assignment  
private:
  void f() {} // ok, just a private function
};

Vous avez eu l'idée. Voyons maintenant comment les agrégats sont spéciaux. Contrairement aux classes non agrégées, ils peuvent être initialisés avec des accolades. {}. Cette syntaxe d'initialisation est généralement connue pour les tableaux, et nous venons d'apprendre que ce sont des agrégats. Alors commençons par eux.

Type array_name[n] = {a1, a2, …, am};

si (m == n)
   le jeth élément du tableau est initialisé avec unje
sinon si (m <n)
les premiers m éléments du tableau sont initialisés avec un1, une2, …, unem et l'autre n - m les éléments sont, si possible, valeur initialisée (voir ci-dessous pour l'explication du terme)
sinon si (m> n)
   le compilateur émettra une erreur
autre  (c'est le cas quand n n'est pas spécifié du tout int a[] = {1, 2, 3};)
 la taille du tableau (n) est supposée être égale à m, donc int a[] = {1, 2, 3}; est équivalent à int a[3] = {1, 2, 3};

Quand un objet de type scalaire (bool, int, char, double, pointeurs, etc.) est valeur initialisée cela signifie qu'il est initialisé avec 0 pour ce type (false pour bool, 0.0 pour double, etc.). Lorsqu'un objet de type classe avec un constructeur par défaut déclaré par l'utilisateur a une valeur initialisée, son constructeur par défaut est appelé. Si le constructeur par défaut est implicitement défini, tous les membres non statiques sont initialisés de façon récursive. Cette définition est imprécise et un peu incorrecte mais elle devrait vous donner l'idée de base. Une référence ne peut pas être initialisée en valeur. L'initialisation de valeur pour une classe non agrégée peut échouer si, par exemple, la classe ne possède pas de constructeur par défaut approprié.

Exemples d'initialisation de tableau:

class A
{
public:
  A(int) {} //no default constructor
};
class B
{
public:
  B() {} //default constructor available
};
int main()
{
  A a1[3] = {A(2), A(1), A(14)}; //OK n == m
  A a2[3] = {A(2)}; //ERROR A has no default constructor. Unable to value-initialize a2[1] and a2[2]
  B b1[3] = {B()}; //OK b1[1] and b1[2] are value initialized, in this case with the default-ctor
  int Array1[1000] = {0}; //All elements are initialized with 0;
  int Array2[1000] = {1}; //Attention: only the first element is 1, the rest are 0;
  bool Array3[1000] = {}; //the braces can be empty too. All elements initialized with false
  int Array4[1000]; //no initializer. This is different from an empty {} initializer in that
  //the elements in this case are not value-initialized, but have indeterminate values 
  //(unless, of course, Array4 is a global array)
  int array[2] = {1, 2, 3, 4}; //ERROR, too many initializers
}

Voyons maintenant comment les classes d'agrégats peuvent être initialisées avec des accolades. Assez de la même manière. Au lieu des éléments du tableau, nous allons initialiser les membres de données non statiques dans l'ordre de leur apparition dans la définition de classe (ils sont tous publics par définition). S'il y a moins d'initialiseurs que de membres, le reste est initialisé en valeur. S'il est impossible d'initialiser la valeur d'un des membres qui n'ont pas été explicitement initialisés, nous obtenons une erreur de compilation. S'il y a plus d'initialiseurs que nécessaire, nous obtenons également une erreur de compilation.

struct X
{
  int i1;
  int i2;
};
struct Y
{
  char c;
  X x;
  int i[2];
  float f; 
protected:
  static double d;
private:
  void g(){}      
}; 

Y y = {'a', {10, 20}, {20, 30}};

Dans l'exemple ci-dessus y.c est initialisé avec 'a', y.x.i1 avec 10, y.x.i2 avec 20, y.i[0] avec 20, y.i[1] avec 30 et y.f est initialisé en valeur, c'est-à-dire initialisé avec 0.0. Le membre statique protégé d n'est pas initialisé du tout, car il est static.

Les unions agrégées sont différentes en ce sens que vous pouvez initialiser uniquement leur premier membre avec des accolades. Je pense que si vous êtes assez avancé en C ++ pour envisager même d'utiliser des syndicats (leur utilisation peut être très dangereuse et doit être considérée avec attention), vous pouvez rechercher vous-même les règles pour les unions dans la norme :).

Maintenant que nous connaissons les spécificités des agrégats, essayons de comprendre les restrictions sur les classes; c'est pourquoi ils sont là. Nous devrions comprendre que l'initialisation de membre par des accolades implique que la classe n'est rien de plus que la somme de ses membres. Si un constructeur défini par l'utilisateur est présent, cela signifie que l'utilisateur doit effectuer un travail supplémentaire pour initialiser les membres. Par conséquent, l'initialisation de l'accolade serait incorrecte. Si des fonctions virtuelles sont présentes, cela signifie que les objets de cette classe ont (sur la plupart des implémentations) un pointeur sur le vtable de la classe, qui est défini dans le constructeur, donc l'initialisation de l'accolade serait insuffisante. Vous pourriez trouver le reste des restrictions de la même manière qu'un exercice :).

Donc assez sur les agrégats. Maintenant, nous pouvons définir un ensemble plus strict de types, à savoir, PODs

Quels sont les POD et pourquoi ils sont spéciaux

Définition formelle de la norme C ++ (C ++ 03 9 §4):

Une structure POD est une classe agrégée   qui n'a pas de données non statiques membres de   type non-POD-struct, non-POD-union (ou   tableau de ces types) ou référence, et   n'a pas d'affectation de copie définie par l'utilisateur   opérateur et pas défini par l'utilisateur   destructeur. De même, une union POD est   une union globale qui n'a pas   membres de données non statiques de type   structure non-POD, non-POD-union (ou   tableau de ces types) ou référence, et   n'a pas d'affectation de copie définie par l'utilisateur   opérateur et pas défini par l'utilisateur   destructeur. Une classe POD est une classe   c'est soit un POD-struct ou un   POD-union.

Wow, celui-ci est plus difficile à analyser, n'est-ce pas? :) Laissons les syndicats (pour les mêmes raisons que ci-dessus) et reformulons de manière un peu plus claire:

Une classe d'agrégat s'appelle un POD si   il n'y a pas d'affectation de copie définie par l'utilisateur   opérateur et destructeur et aucun des   ses membres non-statiques est un non-POD   classe, tableau de non-POD, ou un   référence.

Qu'est-ce que cette définition implique? (Ai-je mentionné COSSE signifie Données anciennes?)

  • Toutes les classes POD sont des agrégats, ou, pour le dire autrement, si une classe n'est pas un agrégat, alors il est sûr que ce n'est pas un POD
  • Les classes, tout comme les structures, peuvent être des POD même si le terme standard est POD-struct pour les deux cas.
  • Tout comme dans le cas des agrégats, peu importe les membres statiques de la classe.

Exemples:

struct POD
{
  int x;
  char y;
  void f() {} //no harm if there's a function
  static std::vector<char> v; //static members do not matter
};

struct AggregateButNotPOD1
{
  int x;
  ~AggregateButNotPOD1() {} //user-defined destructor
};

struct AggregateButNotPOD2
{
  AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class
};

Les classes POD, les unions POD, les types scalaires et les tableaux de ce type sont appelés collectivement Types de POD.
Les POD sont spéciaux à bien des égards. Je vais donner quelques exemples.

  • Les classes POD sont les plus proches des structures C. Contrairement à eux, les POD peuvent avoir des fonctions membres et des membres statiques arbitraires, mais aucun de ces deux ne modifie la disposition de la mémoire de l'objet. Donc, si vous voulez écrire une bibliothèque dynamique plus ou moins portable qui peut être utilisée à partir de C et même de .NET, vous devriez essayer de faire en sorte que toutes vos fonctions exportées prennent et retournent seulement les paramètres des types POD.

  • La durée de vie des objets de type de classe non-POD commence lorsque le constructeur a terminé et se termine lorsque le destructeur est terminé. Pour les classes POD, la durée de vie commence lorsque le stockage de l'objet est occupé et se termine lorsque ce stockage est libéré ou réutilisé.

  • Pour les objets de types POD, il est garanti par la norme que lorsque vous memcpy le contenu de votre objet dans un tableau de caractère char ou unsigned, puis memcpy le contenu dans votre objet, l'objet conservera sa valeur d'origine. Notez qu'il n'y a pas de telle garantie pour les objets de types non-POD. En outre, vous pouvez copier en toute sécurité des objets POD avec memcpy. L'exemple suivant suppose que T est un type POD:

    #define N sizeof(T)
    char buf[N];
    T obj; // obj initialized to its original value
    memcpy(buf, &obj, N); // between these two calls to memcpy,
    // obj might be modified
    memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
    // holds its original value
    
  • instruction goto. Comme vous le savez, il est illégal (le compilateur devrait émettre une erreur) de faire un saut via goto à partir d'un point où une variable n'était pas encore dans la portée à un point où elle est déjà dans la portée. Cette restriction s'applique uniquement si la variable est de type non-POD. Dans l'exemple suivant f()est mal formé alors que g() est bien formé. Notez que le compilateur de Microsoft est trop libéral avec cette règle - il émet juste un avertissement dans les deux cas.

    int f()
    {
      struct NonPOD {NonPOD() {}};
      goto label;
      NonPOD x;
    label:
      return 0;
    }
    
    int g()
    {
      struct POD {int i; char c;};
      goto label;
      POD x;
    label:
      return 0;
    }
    
  • Il est garanti qu'il n'y aura pas de remplissage au début d'un objet POD. En d'autres termes, si le premier membre d'une classe POD A est de type T, vous pouvez reinterpret_cast de A* à T* et obtenez le pointeur sur le premier membre et vice versa.

La liste se rallonge de plus en plus…

Conclusion

Il est important de comprendre exactement ce qu'est un POD, car de nombreuses fonctionnalités linguistiques, comme vous le voyez, se comportent différemment pour elles.


469
2017-08-25 11:48



Quels changements pour C ++ 11?

Agrégats

La définition standard d'un agrégat a légèrement changé, mais c'est toujours la même chose:

Un agrégat est un tableau ou une classe (article 9) sans constructeur fourni par l'utilisateur (12.1),   non accolades ou égales pour les membres de données non statiques (9.2), aucun privé ni protégé   membres de données non statiques (clause 11), pas de classes de base (clause 10) et pas de fonctions virtuelles (10.3).

Ok, qu'est-ce qui a changé?

  1. Auparavant, un agrégat ne pouvait avoir déclaré par l'utilisateur constructeurs, mais maintenant il ne peut pas avoir fourni par l'utilisateur constructeurs. Y a-t-il une différence? Oui, il y a, car maintenant vous pouvez déclarer les constructeurs et défaut leur:

    struct Aggregate {
        Aggregate() = default; // asks the compiler to generate the default implementation
    };
    

    C'est toujours un agrégat car un constructeur (ou une fonction membre spéciale) qui est par défaut sur la première déclaration n'est pas fourni par l'utilisateur.

  2. Maintenant, un agrégat ne peut avoir aucun accolades ou égales pour les membres de données non statiques. Qu'est-ce que ça veut dire? Eh bien, c'est juste parce qu'avec cette nouvelle norme, nous pouvons initialiser les membres directement dans la classe comme ceci:

    struct NotAggregate {
        int x = 5; // valid in C++11
        std::vector<int> s{1,2,3}; // also valid
    };
    

    L'utilisation de cette fonctionnalité fait que la classe n'est plus un agrégat, car elle équivaut à fournir votre propre constructeur par défaut.

Donc, ce qui est un agrégat n'a pas beaucoup changé. C'est toujours la même idée de base, adaptée aux nouvelles fonctionnalités.

Qu'en est-il des POD?

Les POD ont subi beaucoup de changements. Beaucoup de règles précédentes sur les POD ont été assouplies dans cette nouvelle norme, et la façon dont la définition est fournie dans la norme a été radicalement modifiée.

L'idée d'un POD est de capturer essentiellement deux propriétés distinctes:

  1. Il prend en charge l'initialisation statique, et
  2. La compilation d'un POD en C ++ vous donne la même structure de mémoire qu'une structure compilée en C.

Pour cette raison, la définition a été divisée en deux concepts distincts: banal cours et disposition standard classes, car elles sont plus utiles que POD. La norme utilise maintenant rarement le terme POD, préférant le plus spécifique banal et disposition standard concepts

La nouvelle définition dit essentiellement qu'un POD est une classe à la fois triviale et standard, et que cette propriété doit être récursive pour tous les membres de données non statiques:

Une structure POD est une classe non-union qui est à la fois une classe triviale et une classe standard-layout,   et ne possède aucun membre de données non statique de type non-POD struct, union non-POD (ou tableau de ce type).   De même, une union POD est une union qui est à la fois une classe triviale et une classe de mise en page standard, et a   aucun membre de données non statiques de type structure non-POD, union non-POD (ou tableau de tels types).   Une classe POD est une classe qui est une structure POD ou une union POD.

Passons en revue chacune de ces deux propriétés en détail séparément.

Classes triviales

Banal est la première propriété mentionnée ci-dessus: les classes triviales supportent l'initialisation statique. Si une classe est trivialement copiable (un sur-ensemble de classes triviales), il est correct de copier sa représentation sur l’endroit avec des choses comme memcpy et s'attendre à ce que le résultat soit le même.

La norme définit une classe triviale comme suit:

Une classe trivialement copiable est une classe qui:

- n'a pas de constructeur de copie non trivial (12.8),

- n'a pas de constructeur de déplacement non trivial (12.8),

- n'a pas d'opérateur d'assignation de copie non trivial (13.5.3, 12.8),

- n'a pas d'opérateur d'affectation de mouvement non trivial (13.5.3, 12.8), et

- a un destructeur trivial (12.4).

Une classe triviale est une classe qui a un constructeur par défaut trivial (12.1) et est trivialement copiable.

[ Remarque: En particulier, une classe trivialement copiable ou triviale n’a pas de fonctions virtuelles   ou classes de base virtuelles.-End note ]

Alors, quelles sont toutes ces choses insignifiantes et non triviales?

Un constructeur de copie / déplacement pour la classe X est trivial s'il n'est pas fourni par l'utilisateur et si

- la classe X n'a ​​aucune fonction virtuelle (10.3) et aucune classe de base virtuelle (10.1), et

- le constructeur sélectionné pour copier / déplacer chaque sous-objet de classe de base directe est trivial, et

- pour chaque membre de données non statique de X de type classe (ou matrice de celui-ci), le constructeur   sélectionné pour copier / déplacer ce membre est trivial;

sinon, le constructeur de copie / déplacement est non trivial.

Fondamentalement, cela signifie qu'un constructeur de copie ou de déplacement est trivial s'il n'est pas fourni par l'utilisateur, la classe n'a rien de virtuel, et cette propriété est récursive pour tous les membres de la classe et pour la classe de base.

La définition d'un opérateur d'affectation de copie / déplacement trivial est très similaire, remplaçant simplement le mot "constructeur" par "opérateur d'affectation".

Un destructeur trivial a également une définition similaire, avec la contrainte supplémentaire qu'il ne peut pas être virtuel.

Et pourtant, une autre règle similaire existe pour les constructeurs triviaux par défaut, avec l’ajout qu’un constructeur par défaut n’est pas trivial si la classe a des membres de données non statiques avec accolades ou égales, que nous avons vu ci-dessus.

Voici quelques exemples pour tout clarifier:

// empty classes are trivial
struct Trivial1 {};

// all special members are implicit
struct Trivial2 {
    int x;
};

struct Trivial3 : Trivial2 { // base class is trivial
    Trivial3() = default; // not a user-provided ctor
    int y;
};

struct Trivial4 {
public:
    int a;
private: // no restrictions on access modifiers
    int b;
};

struct Trivial5 {
    Trivial1 a;
    Trivial2 b;
    Trivial3 c;
    Trivial4 d;
};

struct Trivial6 {
    Trivial2 a[23];
};

struct Trivial7 {
    Trivial6 c;
    void f(); // it's okay to have non-virtual functions
};

struct Trivial8 {
     int x;
     static NonTrivial1 y; // no restrictions on static members
};

struct Trivial9 {
     Trivial9() = default; // not user-provided
      // a regular constructor is okay because we still have default ctor
     Trivial9(int x) : x(x) {};
     int x;
};

struct NonTrivial1 : Trivial3 {
    virtual void f(); // virtual members make non-trivial ctors
};

struct NonTrivial2 {
    NonTrivial2() : z(42) {} // user-provided ctor
    int z;
};

struct NonTrivial3 {
    NonTrivial3(); // user-provided ctor
    int w;
};
NonTrivial3::NonTrivial3() = default; // defaulted but not on first declaration
                                      // still counts as user-provided
struct NonTrivial5 {
    virtual ~NonTrivial5(); // virtual destructors are not trivial
};

Disposition standard

Disposition standard est la deuxième propriété. La norme mentionne que celles-ci sont utiles pour communiquer avec d'autres langages, et cela parce qu'une classe de disposition standard a la même disposition de mémoire que la structure ou l'union C équivalente.

C'est une autre propriété qui doit être récursive pour les membres et toutes les classes de base. Et comme d'habitude, aucune fonction virtuelle ou classe de base virtuelle n'est autorisée. Cela rendrait la mise en page incompatible avec C.

Une règle détendue ici est que les classes de mise en page standard doivent avoir tous les membres de données non statiques avec le même contrôle d'accès. Auparavant, ils devaient être tous Publique, mais maintenant vous pouvez les rendre privés ou protégés, tant qu'ils sont tout privé ou tout protégé.

En utilisant l'héritage, seulement un classe dans toute l'arbre d'héritage peut avoir des membres de données non statiques, et le premier membre de données non statique ne peut pas être un type de classe de base (ceci pourrait casser les règles d'alias), sinon ce n'est pas une classe de présentation standard.

Voici comment la définition va dans le texte standard:

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

- ne possède pas de données non statiques membres de type non-standard-layout class (ou tableau de ce type)   ou référence,

- n'a pas de fonctions virtuelles (10.3) et pas 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   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 même type que le premier membre de données non statique.

Une structure de mise en page standard est une classe de mise en forme standard définie avec la structure de clé de classe ou   la classe de clé de classe.

Une union de mise en forme standard est une classe de mise en forme standard définie avec l'union de clé de classe.

[ Remarque: Les classes de présentation standard sont utiles pour communiquer avec du code écrit dans d'autres langages de programmation. Leur disposition est spécifiée au 9.2.-End note ]

Et voyons quelques exemples.

// empty classes have standard-layout
struct StandardLayout1 {};

struct StandardLayout2 {
    int x;
};

struct StandardLayout3 {
private: // both are private, so it's ok
    int x;
    int y;
};

struct StandardLayout4 : StandardLayout1 {
    int x;
    int y;

    void f(); // perfectly fine to have non-virtual functions
};

struct StandardLayout5 : StandardLayout1 {
    int x;
    StandardLayout1 y; // can have members of base type if they're not the first
};

struct StandardLayout6 : StandardLayout1, StandardLayout5 {
    // can use multiple inheritance as long only
    // one class in the hierarchy has non-static data members
};

struct StandardLayout7 {
    int x;
    int y;
    StandardLayout7(int x, int y) : x(x), y(y) {} // user-provided ctors are ok
};

struct StandardLayout8 {
public:
    StandardLayout8(int x) : x(x) {} // user-provided ctors are ok
// ok to have non-static data members and other members with different access
private:
    int x;
};

struct StandardLayout9 {
    int x;
    static NonStandardLayout1 y; // no restrictions on static members
};

struct NonStandardLayout1 {
    virtual f(); // cannot have virtual functions
};

struct NonStandardLayout2 {
    NonStandardLayout1 X; // has non-standard-layout member
};

struct NonStandardLayout3 : StandardLayout1 {
    StandardLayout1 x; // first member cannot be of the same type as base
};

struct NonStandardLayout4 : StandardLayout3 {
    int z; // more than one class has non-static data members
};

struct NonStandardLayout5 : NonStandardLayout3 {}; // has a non-standard-layout base class

Conclusion

Avec ces nouvelles règles, beaucoup plus de types peuvent être des POD. Et même si un type n'est pas du type POD, nous pouvons tirer parti de certaines des propriétés du POD (s'il ne s'agit que d'un format trivial ou d'un format standard).

La bibliothèque standard a des traits pour tester ces propriétés dans l'en-tête <type_traits>:

template <typename T>
struct std::is_pod;
template <typename T>
struct std::is_trivial;
template <typename T>
struct std::is_trivially_copyable;
template <typename T>
struct std::is_standard_layout;

380
2017-12-16 18:21



Qu'est-ce qui a changé pour C ++ 14

Nous pouvons nous référer à la Projet de norme C ++ 14 pour référence.

Agrégats

Ceci est couvert dans la section 8.5.1  Agrégats ce qui nous donne la définition suivante:

Un agrégat est un tableau ou une classe (article 9) sans données fournies par l'utilisateur   constructeurs (12.1), aucun membre de données privé ou protégé non statique   (Article 11), pas de classes de base (Article 10), et pas de fonctions virtuelles   (10.3).

Le seul changement est en train d'ajouter initialisateurs membres en classe ne fait pas de classe un non-agrégat. Donc, l'exemple suivant de Initialisation d'agrégat C ++ 11 pour les classes avec initialiseurs de membre:

struct A
{
  int a = 3;
  int b = 3;
};

n'était pas un agrégat en C ++ 11 mais en C ++ 14. Ce changement est couvert dans N3605: Initialiseurs et agrégats de membres, qui a le résumé suivant:

Bjarne Stroustrup et Richard Smith ont soulevé un problème concernant les agrégats   l'initialisation et les initialisateurs de membres ne fonctionnent pas ensemble. Ce   L'article propose de résoudre le problème en adoptant le libellé proposé par Smith   qui supprime une restriction que les agrégats ne peuvent pas avoir   initialiseurs de membres.

POD reste le même

La définition de POD (plaine d'anciennes données) struct est couverte dans la section 9  Des classes qui dit:

Une structure POD110 est une classe non syndiquée qui est à la fois une classe triviale et   une classe de mise en page standard et n'a pas de membres de données non statiques de type   structure non-POD, union non-POD (ou tableau de tels types). De même, un   POD union est un syndicat qui est à la fois une classe triviale et une   classe standard-mise en page, et n'a pas de membres de données non statiques de type   structure non-POD, union non-POD (ou tableau de tels types). Une classe POD est   une classe qui est soit une structure POD ou une union POD.

qui est le même libellé que C ++ 11.


74
2018-02-28 23:26



pouvez-vous s'il vous plaît élaborer les règles suivantes:

J'essaierai:

a) les classes de mise en page standard doivent avoir tous les membres de données non statiques avec le même contrôle d'accès

C'est simple: tous les membres de données non statiques doivent tout être public, private, ou protected. Tu ne peux pas en avoir public et certaines private.

Leur raisonnement va dans le sens de la distinction entre «mise en page standard» et «mise en page non standard». A savoir, donner au compilateur la liberté de choisir comment mettre les choses en mémoire. Il ne s'agit pas seulement de pointeurs vtable.

À l'époque où ils ont normalisé le C ++ en 98, ils devaient essentiellement prédire comment les gens l'implémenteraient. Bien qu'ils aient acquis une certaine expérience de l'implémentation avec différentes versions de C ++, ils n'étaient pas certains des choses. Ils ont donc décidé d'être prudents: donner aux compilateurs autant de liberté que possible.

C'est pourquoi la définition de POD en C ++ 98 est si stricte. Cela a donné aux compilateurs C ++ une grande latitude sur la disposition des membres pour la plupart des classes. Fondamentalement, les types POD étaient destinés à être des cas spéciaux, quelque chose que vous avez spécifiquement écrit pour une raison.

Quand on travaillait sur C ++ 11, ils avaient beaucoup plus d'expérience avec les compilateurs. Et ils ont réalisé que ... les auteurs de compilateurs C ++ sont vraiment paresseux. Ils avaient toute cette liberté, mais ils n'ont pas faire rien avec ça.

Les règles de mise en page standard codifient plus ou moins la pratique courante: la plupart des compilateurs n'ont pas vraiment besoin de changer grand chose pour les implémenter (en dehors peut-être de certains éléments pour les traits de caractères correspondants).

Maintenant, quand il est venu à public/private, les choses sont différentes. La liberté de réorganiser les membres public contre. private peut en fait importer au compilateur, en particulier dans les versions de débogage. Et comme le point de la mise en page standard est la compatibilité avec les autres langages, vous ne pouvez pas avoir une mise en page ou un lancement différent de la mise en page.

Ensuite, il y a le fait que cela ne blesse pas vraiment l'utilisateur. Si vous créez une classe encapsulée, les chances sont bonnes que tous vos membres de données soient private en tous cas. Vous n'exposez généralement pas les membres de données publics sur des types entièrement encapsulés. Donc, ce ne serait qu'un problème pour les quelques utilisateurs qui veulent faire cela, qui veulent cette division.

Donc ce n'est pas une grosse perte.

b) une seule classe de l'arbre d'héritage entier peut avoir des membres de données non statiques,

La raison de ceci revient à la normalisation de la mise en page standard: pratique courante.

Il y a non pratique courante quand il s'agit d'avoir deux membres d'un arbre d'héritage qui stockent réellement des choses. Certains mettent la classe de base avant la dérivée, d'autres la font dans l'autre sens. De quelle façon commandez-vous les membres s'ils proviennent de deux classes de base? Etc. Les compilateurs divergent grandement sur ces questions.

De plus, grâce à la règle du zéro / un / infini, une fois que vous dites que vous pouvez avoir deux classes avec des membres, vous pouvez en dire autant que vous voulez. Cela nécessite l'ajout d'un grand nombre de règles de mise en page pour savoir comment gérer cela. Vous devez dire comment fonctionne l'héritage multiple, quelles classes placent leurs données avant les autres classes, etc. C'est beaucoup de règles, pour très peu de gain matériel.

Vous ne pouvez pas créer tout ce qui n'a pas de fonctions virtuelles et une disposition standard de constructeur par défaut.

et le premier membre de données non statique ne peut pas être d'un type de classe de base (cela pourrait casser les règles d'alias).

Je ne peux pas vraiment parler à celui-ci. Je ne suis pas assez instruit dans les règles d'aliasing de C ++ pour vraiment le comprendre. Mais cela a quelque chose à voir avec le fait que le membre de base partagera la même adresse que la classe de base elle-même. C'est:

struct Base {};
struct Derived : Base { Base b; };

Derived d;
static_cast<Base*>(&d) == &d.b;

Et cela va probablement à l'encontre des règles d'alias de C ++. En quelque sorte.

Cependant, considérez ceci: combien utile pourrait avoir la capacité de faire ceci jamais réellement être? Comme une seule classe peut avoir des membres de données non statiques, alors Derived doit être cette classe (puisqu'il a un Base en tant que membre). Alors Base  doit être vide (de données). Et si Base est vide, aussi bien que une classe de base ... pourquoi avoir un membre de données du tout?

Depuis Base est vide, il n'a pas d'état. Ainsi, toutes les fonctions membres non statiques feront ce qu'ils font en fonction de leurs paramètres, pas leur this aiguille.

Donc encore une fois: pas de grosse perte.


36
2018-06-10 17:06



Changements dans C ++ 17

Télécharger la version finale de la norme internationale C ++ 17 ici.

Agrégats

C ++ 17 développe et améliore les agrégats et l'initialisation des agrégats. La bibliothèque standard comprend également un std::is_aggregate type trait classe. Voici la définition formelle des sections 11.6.1.1 et 11.6.1.2 (références internes élidées):

Un agrégat est un tableau ou une classe avec
   - aucun constructeur fourni par l'utilisateur, explicite ou hérité,
   - aucun membre de données privé ou protégé non statique,
   - pas de fonctions virtuelles, et
   - Pas de classes de base virtuelles, privées ou protégées.
  [Remarque: l'initialisation d'agrégat n'autorise pas l'accès aux membres ou aux constructeurs de classe de base protégée et privée. -end note]
  Les éléments d'un agrégat sont:
   - pour un tableau, les éléments du tableau en ordre croissant, ou
   - pour une classe, les classes de base directes dans l'ordre de déclaration, suivies des données directes non statiques qui ne sont pas membres d'une union anonyme, dans l'ordre de déclaration.

Qu'est ce qui a changé?

  1. Les agrégats peuvent désormais avoir des classes de base publiques et non virtuelles. En outre, les classes de base ne sont pas obligatoirement des agrégats. S'ils ne sont pas des agrégats, ils sont initialisés par liste.
struct B1 // not a aggregate
{
    int i1;
    B1(int a) : i1(a) { }
};
struct B2
{
    int i2;
    B2() = default;
};
struct M // not an aggregate
{
    int m;
    M(int a) : m(a) { }
};
struct C : B1, B2
{
    int j;
    M m;
    C() = default;
};
C c { { 1 }, { 2 }, 3, { 4 } };
cout
    << "is C aggregate?: " << (std::is_aggregate::value ? 'Y' : 'N')
    << " i1: " << c.i1 << " i2: " << c.i2
    << " j: " << c.j << " m.m: " << c.m.m << endl;

//stdout: is C aggregate?: Y, i1=1 i2=2 j=3 m.m=4
  1. Les constructeurs explicites par défaut sont interdits
struct D // not an aggregate
{
    int i = 0;
    D() = default;
    explicit D(D const&) = default;
};
  1. Les constructeurs hérités ne sont pas autorisés
struct B1
{
    int i1;
    B1() : i1(0) { }
};
struct C : B1 // not an aggregate
{
    using B1::B1;
};


Classes triviales

La définition de la classe triviale a été retravaillée en C ++ 17 pour résoudre plusieurs problèmes non traités dans C ++ 14. Les changements étaient de nature technique. Voici la nouvelle définition à 12.0.6 (références internes élidées):

Une classe trivialement copiable est une classe:
   - où chaque constructeur de copie, constructeur de déplacement, opérateur d'assignation de copie et opérateur d'assignation de déplacement est supprimé ou trivial,
   - qui possède au moins un constructeur de copie non supprimé, un constructeur de déplacement, un opérateur d'affectation de copie ou un opérateur d'affectation de déplacement, et
   - qui a un destructeur trivial, non supprimé.
  Une classe triviale est une classe trivialement copiable et comportant un ou plusieurs constructeurs par défaut, tous triviaux ou supprimés et dont au moins l'un n'est pas supprimé. [Note: en particulier, un trivialement copiable   ou classe triviale n'a pas de fonctions virtuelles ou de classes de base virtuelles.-fin note]

Changements:

  1. Sous C ++ 14, pour qu'une classe soit triviale, la classe ne pouvait avoir aucun constructeur / attribut de constructeur de copie / déplacement non trivial. Cependant, alors implicitement déclaré comme constructeur / opérateur par défaut pourrait être non trivial et pourtant défini comme supprimé car, par exemple, la classe contenait un sous-objet de type de classe qui ne pouvait pas être copié / déplacé. La présence d'un tel constructeur / opérateur non trivial défini comme supprimé entraînerait la non-trivialité de toute la classe. Un problème similaire existait avec les destructeurs. C ++ 17 précise que la présence de tels constructeurs / opérateurs n'entraîne pas la copie non triviale de la classe, donc non triviale, et qu'une classe trivialement copiable doit avoir un destructeur trivial et non supprimé. DR1734, DR1928
  2. C ++ 14 permettait à une classe trivialement copiable, donc à une classe triviale, de faire en sorte que chaque opérateur / constructeur d'assignation copie / déplacement soit déclaré comme supprimé. Si la classe était également standard, elle pouvait cependant être légalement copiée / déplacée avec std::memcpy. C'était une contradiction sémantique, car en définissant comme supprimés tous les opérateurs / opérateurs, le créateur de la classe voulait clairement que la classe ne puisse pas être copiée / déplacée, mais la classe répondait toujours à la définition d'une classe trivialement copiable. Par conséquent, en C ++ 17, nous avons une nouvelle clause indiquant que la classe trivialement copiable doit avoir au moins un opérateur / attribut constructeur / affectation trivial, non supprimé (mais pas nécessairement accessible au public). Voir N4148, DR1734
  3. Le troisième changement technique concerne un problème similaire avec les constructeurs par défaut. Sous C ++ 14, une classe peut avoir des constructeurs par défaut triviaux qui ont été définis implicitement comme supprimés, tout en restant une classe triviale. La nouvelle définition précise qu'une classe triviale doit avoir au moins un constructeur par défaut trivial, non supprimé. Voir DR1496

Classes de présentation standard

La définition de la présentation standard a également été modifiée pour traiter les rapports de défauts. Encore une fois, les changements étaient de nature technique. Voici le texte de la norme (12.0.7). Comme précédemment, les références internes sont élidées:

Une classe S est une classe standard si elle:
   - n'a pas de membre de données non statiques de type classe de mise en page non standard (ou tableau de tels types) ou de référence,
   - n'a aucune fonction virtuelle et aucune classe de base virtuelle,
   - a le même contrôle d'accès pour tous les membres de données non statiques,
   - n'a pas de classes de base de mise en page non standard,
   - a au plus un sous-objet de classe de base d'un type donné,
   - a tous les membres de données non statiques et les champs de bits dans la classe et ses classes de base déclarées pour la première fois dans la même classe, et
   - n'a pas d'élément de l'ensemble M (S) des types (définis ci-dessous) en tant que classe de base.108
  M (X) est défini comme suit:
   - Si X est un type de classe non-union sans aucun membre de données non statiques (éventuellement hérité), l'ensemble M (X) est vide.
   - Si X est un type de classe non-union dont le premier membre de données non statique a le type X0 (où ce membre peut être une union anonyme), l'ensemble M (X) se compose de X0 et des éléments de M (X0).
   - Si X est un type d’union, l’ensemble M (X) est l’union de tous les M (Ui) et de l’ensemble contenant toutes les Ui, où chaque Ui est le type de la donnée membre non statique de X.
   - Si X est un type de tableau avec l'élément de type Xe, l'ensemble M (X) est constitué de Xe et des éléments de M (Xe).
   - Si X est un type non-classe, non-tableau, l'ensemble M (X) est vide.
  [Remarque: M (X) est l'ensemble des types de tous les sous-objets non de classe de base garantis dans une classe de présentation standard comme étant à un décalage nul dans X. -end note]
  [ Exemple:

struct B { int i; }; // standard-layout class
struct C : B { }; // standard-layout class
struct D : C { }; // standard-layout class
struct E : D { char : 4; }; // not a standard-layout class
struct Q {};
struct S : Q { };
struct T : Q { };
struct U : S, T { }; // not a standard-layout class
  -end exemple]
  108) Cela garantit que deux sous-objets ayant le même type de classe et appartenant au même objet le plus dérivé ne sont pas affectés à la même adresse.

Changements:

  1. Précisé que l'exigence selon laquelle une seule classe de l'arbre de dérivation "possède" des membres de données non statiques fait référence à une classe dans laquelle ces membres de données sont déclarés en premier, et non des classes où ils peuvent être hérités. . Également précisé qu'une classe de mise en page standard "a au plus un sous-objet de classe de base de n'importe quel type donné." Voir DR1813, DR1881
  2. La définition de standard-layout n'a jamais permis au type d'une classe de base d'être du même type que le premier membre de données non-statique. C'est pour éviter une situation où un membre de données à l'offset zéro a le même type que n'importe quelle classe de base. La norme C ++ 17 fournit une définition récursive plus rigoureuse de "l'ensemble des types de tous les sous-objets non de classe de base garantis dans une classe de présentation standard comme étant à un décalage nul" afin d'interdire de tels types d'être le type de n'importe quelle classe de base. Voir DR1672, DR2120.

5