Quels sont les types de POD en C ++?


979

J'ai rencontré ce terme de type POD à quelques reprises.
Qu'est-ce que ça veut dire?



5
veuillez consulter chat.stackoverflow.com/transcript/message/213026#213026 et les messages du lendemain pour une discussion sur la réponse acceptée
Johannes Schaub - litb


@ paxos1977: Veuillez modifier votre sélection de "solution" (actuellement la réponse de Hewgill) afin qu'une réponse fondamentalement erronée n'induise pas en erreur les googleurs qui se retrouvent ici.
Bravo et hth. - Alf

Nous avons conclu qu'une chaîne de style c n'est PAS un type POD car 1.) le pointeur n'est pas contigu aux données de la chaîne, et 2.) pour faire d'une chaîne un type POD, vous devez vous assurer que le type avait un caractère à zéro dans la taille prédéfinie du type POD, conduisant à un comportement indéfini.

Réponses:


695

POD signifie Plain Old Data - c'est-à-dire une classe (qu'elle soit définie avec le mot-clé structou le mot-clé class) sans constructeurs, destructeurs et fonctions de membres virtuels. L'article de Wikipédia sur POD va un peu plus en détail et le définit comme:

Une structure de données ancienne simple en C ++ est une classe agrégée qui contient uniquement des PODS en tant que membres, n'a pas de destructeur défini par l'utilisateur, aucun opérateur d'affectation de copie défini par l'utilisateur et aucun membre non statique de type pointeur sur membre.

Plus de détails peuvent être trouvés dans cette réponse pour C ++ 98/03 . C ++ 11 a changé les règles entourant POD, les assouplissant considérablement, nécessitant ainsi une réponse de suivi ici .


35
Il y a une différence. Les types intrinsèques sont les primitives de langage "intégrées". Les types de POD sont ceux-ci, plus les agrégations de ceux-ci (et d'autres POD).
Adam Wright

59
Les types POD ont des caractéristiques que les types non-POD n'ont pas. Par exemple, si vous avez une structure globale, const, de type POD, vous pouvez initialiser son contenu avec la notation d'accolade, il est mis en mémoire morte, et aucun code n'a besoin d'être généré pour l'initialiser (constructeur ou autre), car cela fait partie de l'image du programme. Ceci est important pour les personnes embarquées qui ont souvent des contraintes strictes sur la RAM, la ROM ou le Flash.
Mike DeSimone

35
En C ++ 11, vous pouvez faire std :: is_pod <MyType> () pour dire si MyType est POD.
allyourcode

7
Le rapport technique de Bjarne Stroustrup sur les performances C ++ déclare que la norme C ++ décrit un POD comme étant " un type de données compatible avec le type de données équivalent en C dans la présentation, l'initialisation et sa capacité à être copié avec memcpy ". Peut-être faudrait-il faire une distinction entre un type de POD et une structure de POD.
user34660

6
−1 Cette réponse est toujours fondamentalement erronée et trompeuse au 16 août 2016: les types POD ne sont pas limités à des types de classe.
Bravo et hth. - Alf

353

De manière très informelle:

Un POD est un type (y compris les classes) où le compilateur C ++ garantit qu'il n'y aura pas de "magie" dans la structure: par exemple, des pointeurs cachés vers des tables vtables, des décalages qui sont appliqués à l'adresse lorsqu'elle est convertie en d'autres types ( au moins si le POD de la cible aussi), constructeurs ou destructeurs. En gros, un type est un POD lorsque les seules choses qu'il contient sont des types intégrés et des combinaisons d'entre eux. Le résultat est quelque chose qui "agit comme" un type C.

Moins informellement:

  • int, char, wchar_t, bool, float, doubleSont PODs, comme le sont long/shortet les signed/unsignedversions d'entre eux.
  • les pointeurs (y compris pointeur vers fonction et pointeur vers membre) sont des POD,
  • enums sont des POD
  • a constou volatilePOD est un POD.
  • a class, structou uniondes POD est un POD à condition que tous les membres de données non statiques le soient public, et qu'il n'a pas de classe de base et pas de constructeurs, destructeurs ou méthodes virtuelles. Les membres statiques n'empêchent pas quelque chose d'être un POD selon cette règle. Cette règle a changé en C ++ 11 et certains membres privés sont autorisés: une classe avec tous les membres privés peut-elle être une classe POD?
  • Wikipedia a tort de dire qu'un POD ne peut pas avoir de membres de type pointeur vers membre. Ou plutôt, c'est correct pour la formulation C ++ 98, mais TC1 a expliqué explicitement que les pointeurs vers membre sont POD.

Formellement (norme C ++ 03):

3.9 (10): "Les types arithmétiques (3.9.1), les types d'énumération, les types de pointeur et le pointeur vers les types de membre (3.9.2) et les versions qualifiées par cv de ces types (3.9.3) sont collectivement des types scalaires d'appelants. Scalar types, types POD-struct, types POD-union (article 9), tableaux de ces types et versions qualifiées cv de ces types (3.9.3) sont collectivement appelés types POD "

9 (4): "Un POD-struct est une classe agrégée qui n'a pas de membres de données non statiques de type non-POD-struct, non-POD-union (ou tableau de ces types) ou référence, et n'a pas de définir un opérateur de copie et aucun destructeur défini par l'utilisateur. De même, une union POD est une union agrégée qui n'a pas de membres de données non statiques de type non-POD-struct, non-POD-union (ou tableau de ces types) ou référence, et n'a aucun opérateur de copie défini par l'utilisateur et aucun destructeur défini par l'utilisateur.

8.5.1 (1): "Un agrégat est un tableau ou une classe (article 9) sans constructeurs déclarés par l'utilisateur (12.1), aucun membre de données non statique privé ou protégé (article 11), aucune classe de base (article 10) et aucune fonction virtuelle (10.3). "


3
Vous avez formel / moins formel. Vous pouvez ajouter une règle d'or. Types intégrés et agrégations de types intégrés (ou quelque chose comme ça). En plus d'obtenir la définition exacte, nous devons rendre les connaissances faciles à utiliser.
Martin York

1
Vous vous trompez un peu sur le bit "offsets when cast_to another type". Ces décalages sont appliqués lors de la conversion vers une classe de base ou dérivée. Ainsi, si vous effectuez un cast à partir d'un pointeur de classe de base POD vers une classe non dérivée de POD, vous pouvez toujours rencontrer un ajustement.
MSalters

1
@Steve Jessop: Pourquoi devons-nous faire la différence entre les POD et les non-POD?
Lazer

6
@Lazer: c'est une toute autre question, "comment se comportent les POD?" par opposition à "que signifie POD?". En résumé, la différence concerne l'initialisation (et donc également l'utilisation de memcpy pour dupliquer des objets), la compatibilité avec la mise en page C struct pour ce compilateur et le pointage vers le haut et vers le bas. Les POD "agissent comme des types C", les non-POD ne sont pas garantis de le faire. Donc, si vous voulez que votre type agisse de manière portable comme une structure C, vous devez vous assurer qu'il s'agit de POD, vous devez donc connaître la différence.
Steve Jessop

4
@muntoo: ça a été, vraiment je commentais la réponse qui cite des informations obsolètes de Wikipedia. Je pourrais modifier cette réponse, je suppose, mais je sens mal si je vais modifier la réponse des autres pour être d'accord avec la mienne, peu importe à quel point je pense avoir raison.
Steve Jessop

21

Plain Old Data

Bref, il est tout intégré types de données (par exemple int, char, float, long, unsigned char, double, etc.) et toute agrégation des données POD. Oui, c'est une définition récursive. ;)

Pour être plus clair, un POD est ce que nous appelons "une structure": une unité ou un groupe d'unités qui ne font que stocker des données.


13
Il est vrai que nous les appelons parfois «un struct». Cependant, nous avons toujours tort de le faire, car une structure n'est pas nécessairement un type POD.
Steve Jessop

7
évidemment ... struct et classe sont presque équivalents, mais dans "l'entreprise" nous appelons 'a struct' un simple collecteur de données, généralement sans ctors ni dtor, généralement avec une sémantique de valeur ...
ugasoft

2
Pour moi, c'était C ++ mal de rendre struct identique au mot clé class ou proche de: struct ajoute uniquement l'accès par défaut public à la classe. J'étais plus simple de faire des structures de type C et nous aurions eu des POD au jour 0 de c ++.
user1708042

ugasoft: votre réponse peut être trompeuse - votre commentaire explique le détail manquant selon lequel il est utilisé comme ça dans la pratique, plutôt que standard. Whoa, 8 ans, tu es même ici? ;-)
hauron

À l'exception d'une chaîne, car vous ne pouvez pas la copier avec memcpy sans d'abord déterminer la longueur de la chaîne.

12

Si je comprends bien, POD (PlainOldData) n'est qu'une donnée brute - il n'a pas besoin:

  • à construire,
  • être détruit,
  • d'avoir des opérateurs personnalisés.
  • Ne doit pas avoir de fonctions virtuelles,
  • et ne doit pas remplacer les opérateurs.

Comment vérifier si quelque chose est un POD? Eh bien, il y a une structure pour cela appelée std::is_pod:

namespace std {
// Could use is_standard_layout && is_trivial instead of the builtin.
template<typename _Tp>
  struct is_pod
  : public integral_constant<bool, __is_pod(_Tp)>
  { };
}

(De l'en-tête type_traits)


Référence:


2
Incorrect, un type POD peut avoir des fonctions membres ou des opérateurs surchargés. (Mais il peut ne pas avoir de fonctions de membre virtuel.)
Colin D Bennett

@ColinDBennett Oui, c'est vrai. Désolé pour la confusion. Édité dans / hors de la réponse.
набиячлэвэли

10

Un objet POD (plain old data) possède l'un de ces types de données - un type fondamental, un pointeur, une union, une structure, un tableau ou une classe - sans constructeur. Inversement, un objet non POD est un objet pour lequel un constructeur existe. Un objet POD commence sa durée de vie lorsqu'il obtient un stockage avec la taille appropriée pour son type et sa durée de vie se termine lorsque le stockage de l'objet est soit réutilisé soit désalloué.

Les types PlainOldData ne doivent pas non plus avoir:

  • Fonctions virtuelles (soit les leurs, soit héritées)
  • Classes de base virtuelles (directes ou indirectes).

Une définition plus lâche de PlainOldData inclut des objets avec des constructeurs; mais exclut ceux avec quoi que ce soit virtuel. Le problème important avec les types PlainOldData est qu'ils ne sont pas polymorphes. L'héritage peut être effectué avec des types POD, mais il ne doit être effectué que pour ImplementationInheritance (réutilisation de code) et non pour le polymorphisme / sous-typage.

Une définition courante (mais pas strictement correcte) est qu'un type PlainOldData est tout ce qui n'a pas de VeeTable.


La réponse de Yuor est très bonne, mais cette question a accepté la réponse il y a 8 ans, ainsi que plusieurs autres bonnes réponses. Vous pouvez contribuer davantage à SO si vous utilisez vos connaissances pour répondre à des questions auxquelles vous n'avez pas encore répondu)))
mvidelgauz

10

Pourquoi devons-nous faire la différence entre les POD et les non-POD?

C ++ a commencé sa vie comme une extension de C. Alors que le C ++ moderne n'est plus un sur-ensemble strict de C, les gens s'attendent toujours à un haut niveau de compatibilité entre les deux.

En gros, un type POD est un type compatible avec C et peut-être tout aussi important est compatible avec certaines optimisations ABI.

Pour être compatible avec C, nous devons satisfaire deux contraintes.

  1. La disposition doit être la même que le type C correspondant.
  2. Le type doit être transmis et renvoyé par les fonctions de la même manière que le type C correspondant.

Certaines fonctionnalités C ++ sont incompatibles avec cela.

Les méthodes virtuelles nécessitent que le compilateur insère un ou plusieurs pointeurs dans les tables de méthodes virtuelles, ce qui n'existe pas en C.

Les constructeurs de copie définis par l'utilisateur, les constructeurs de déplacement, les affectations de copie et les destructeurs ont des implications sur le passage et le retour des paramètres. De nombreux ABI C passent et renvoient de petits paramètres dans les registres, mais les références passées au constructeur / assignation / destructeur défini par l'utilisateur ne peuvent fonctionner qu'avec des emplacements de mémoire.

Il est donc nécessaire de définir quels types peuvent être censés être "compatibles C" et quels types ne le peuvent pas. C ++ 03 était quelque peu trop strict à cet égard, tout constructeur défini par l'utilisateur désactiverait les constructeurs intégrés et toute tentative de les rajouter entraînerait leur définition par l'utilisateur et donc le type non pod. C ++ 11 a ouvert les choses un peu, en permettant à l'utilisateur de réintroduire les constructeurs intégrés.


8

Exemples de tous les cas non-POD avec static_assertde C ++ 11 à C ++ 17 et effets POD

std::is_pod a été ajouté en C ++ 11, considérons donc cette norme à partir de maintenant.

std::is_podsera supprimé de C ++ 20 comme mentionné sur https://stackoverflow.com/a/48435532/895245 , mettons à jour cela à mesure que le support arrive pour les remplacements.

Les restrictions POD sont devenues de plus en plus assouplies au fur et à mesure que la norme évoluait, je vise à couvrir toutes les relaxations dans l'exemple à travers ifdefs.

libstdc ++ a un tout petit peu de test sur: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc mais il trop peu. Mainteneurs: veuillez fusionner ceci si vous lisez ce post. Je suis paresseux pour vérifier tous les projets de suite de tests C ++ mentionnés sur: /software/199708/is-there-a-compliance-test-for-c-compilers

#include <type_traits>
#include <array>
#include <vector>

int main() {
#if __cplusplus >= 201103L
    // # Not POD
    //
    // Non-POD examples. Let's just walk all non-recursive non-POD branches of cppreference.
    {
        // Non-trivial implies non-POD.
        // https://en.cppreference.com/w/cpp/named_req/TrivialType
        {
            // Has one or more default constructors, all of which are either
            // trivial or deleted, and at least one of which is not deleted.
            {
                // Not trivial because we removed the default constructor
                // by using our own custom non-default constructor.
                {
                    struct C {
                        C(int) {}
                    };
                    static_assert(std::is_trivially_copyable<C>(), "");
                    static_assert(!std::is_trivial<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }

                // No, this is not a default trivial constructor either:
                // https://en.cppreference.com/w/cpp/language/default_constructor
                //
                // The constructor is not user-provided (i.e., is implicitly-defined or
                // defaulted on its first declaration)
                {
                    struct C {
                        C() {}
                    };
                    static_assert(std::is_trivially_copyable<C>(), "");
                    static_assert(!std::is_trivial<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }
            }

            // Not trivial because not trivially copyable.
            {
                struct C {
                    C(C&) {}
                };
                static_assert(!std::is_trivially_copyable<C>(), "");
                static_assert(!std::is_trivial<C>(), "");
                static_assert(!std::is_pod<C>(), "");
            }
        }

        // Non-standard layout implies non-POD.
        // https://en.cppreference.com/w/cpp/named_req/StandardLayoutType
        {
            // Non static members with different access control.
            {
                // i is public and j is private.
                {
                    struct C {
                        public:
                            int i;
                        private:
                            int j;
                    };
                    static_assert(!std::is_standard_layout<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }

                // These have the same access control.
                {
                    struct C {
                        private:
                            int i;
                            int j;
                    };
                    static_assert(std::is_standard_layout<C>(), "");
                    static_assert(std::is_pod<C>(), "");

                    struct D {
                        public:
                            int i;
                            int j;
                    };
                    static_assert(std::is_standard_layout<D>(), "");
                    static_assert(std::is_pod<D>(), "");
                }
            }

            // Virtual function.
            {
                struct C {
                    virtual void f() = 0;
                };
                static_assert(!std::is_standard_layout<C>(), "");
                static_assert(!std::is_pod<C>(), "");
            }

            // Non-static member that is reference.
            {
                struct C {
                    int &i;
                };
                static_assert(!std::is_standard_layout<C>(), "");
                static_assert(!std::is_pod<C>(), "");
            }

            // Neither:
            //
            // - has no base classes with non-static data members, or
            // - has no non-static data members in the most derived class
            //   and at most one base class with non-static data members
            {
                // Non POD because has two base classes with non-static data members.
                {
                    struct Base1 {
                        int i;
                    };
                    struct Base2 {
                        int j;
                    };
                    struct C : Base1, Base2 {};
                    static_assert(!std::is_standard_layout<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }

                // POD: has just one base class with non-static member.
                {
                    struct Base1 {
                        int i;
                    };
                    struct C : Base1 {};
                    static_assert(std::is_standard_layout<C>(), "");
                    static_assert(std::is_pod<C>(), "");
                }

                // Just one base class with non-static member: Base1, Base2 has none.
                {
                    struct Base1 {
                        int i;
                    };
                    struct Base2 {};
                    struct C : Base1, Base2 {};
                    static_assert(std::is_standard_layout<C>(), "");
                    static_assert(std::is_pod<C>(), "");
                }
            }

            // Base classes of the same type as the first non-static data member.
            // TODO failing on GCC 8.1 -std=c++11, 14 and 17.
            {
                struct C {};
                struct D : C {
                    C c;
                };
                //static_assert(!std::is_standard_layout<C>(), "");
                //static_assert(!std::is_pod<C>(), "");
            };

            // C++14 standard layout new rules, yay!
            {
                // Has two (possibly indirect) base class subobjects of the same type.
                // Here C has two base classes which are indirectly "Base".
                //
                // TODO failing on GCC 8.1 -std=c++11, 14 and 17.
                // even though the example was copy pasted from cppreference.
                {
                    struct Q {};
                    struct S : Q { };
                    struct T : Q { };
                    struct U : S, T { };  // not a standard-layout class: two base class subobjects of type Q
                    //static_assert(!std::is_standard_layout<U>(), "");
                    //static_assert(!std::is_pod<U>(), "");
                }

                // Has all non-static data members and bit-fields declared in the same class
                // (either all in the derived or all in some base).
                {
                    struct Base { int i; };
                    struct Middle : Base {};
                    struct C : Middle { int j; };
                    static_assert(!std::is_standard_layout<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }

                // None of the base class subobjects has the same type as
                // for non-union types, as the first non-static data member
                //
                // TODO: similar to the C++11 for which we could not make a proper example,
                // but with recursivity added.

                // TODO come up with an example that is POD in C++14 but not in C++11.
            }
        }
    }

    // # POD
    //
    // POD examples. Everything that does not fall neatly in the non-POD examples.
    {
        // Can't get more POD than this.
        {
            struct C {};
            static_assert(std::is_pod<C>(), "");
            static_assert(std::is_pod<int>(), "");
        }

        // Array of POD is POD.
        {
            struct C {};
            static_assert(std::is_pod<C>(), "");
            static_assert(std::is_pod<C[]>(), "");
        }

        // Private member: became POD in C++11
        // /programming/4762788/can-a-class-with-all-private-members-be-a-pod-class/4762944#4762944
        {
            struct C {
                private:
                    int i;
            };
#if __cplusplus >= 201103L
            static_assert(std::is_pod<C>(), "");
#else
            static_assert(!std::is_pod<C>(), "");
#endif
        }

        // Most standard library containers are not POD because they are not trivial,
        // which can be seen directly from their interface definition in the standard.
        // /programming/27165436/pod-implications-for-a-struct-which-holds-an-standard-library-container
        {
            static_assert(!std::is_pod<std::vector<int>>(), "");
            static_assert(!std::is_trivially_copyable<std::vector<int>>(), "");
            // Some might be though:
            // /programming/3674247/is-stdarrayt-s-guaranteed-to-be-pod-if-t-is-pod
            static_assert(std::is_pod<std::array<int, 1>>(), "");
        }
    }

    // # POD effects
    //
    // Now let's verify what effects does PODness have.
    //
    // Note that this is not easy to do automatically, since many of the
    // failures are undefined behaviour.
    //
    // A good initial list can be found at:
    // /programming/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special/4178176#4178176
    {
        struct Pod {
            uint32_t i;
            uint64_t j;
        };
        static_assert(std::is_pod<Pod>(), "");

        struct NotPod {
            NotPod(uint32_t i, uint64_t j) : i(i), j(j) {}
            uint32_t i;
            uint64_t j;
        };
        static_assert(!std::is_pod<NotPod>(), "");

        // __attribute__((packed)) only works for POD, and is ignored for non-POD, and emits a warning
        // /programming/35152877/ignoring-packed-attribute-because-of-unpacked-non-pod-field/52986680#52986680
        {
            struct C {
                int i;
            };

            struct D : C {
                int j;
            };

            struct E {
                D d;
            } /*__attribute__((packed))*/;

            static_assert(std::is_pod<C>(), "");
            static_assert(!std::is_pod<D>(), "");
            static_assert(!std::is_pod<E>(), "");
        }
    }
#endif
}

GitHub en amont .

Testé avec:

for std in 11 14 17; do echo $std; g++-8 -Wall -Werror -Wextra -pedantic -std=c++$std pod.cpp; done

sur Ubuntu 18.04, GCC 8.2.0.


4

Le concept de POD et le trait de type std::is_podseront déconseillés en C ++ 20. Voir cette question pour plus d'informations.


-7

Avec C ++, Plain Old Data ne signifie pas seulement que des choses comme int, char, etc. sont les seuls types utilisés. Plain Old Data signifie vraiment dans la pratique que vous pouvez prendre un struct memcpy d'un emplacement en mémoire à un autre et les choses fonctionneront exactement comme vous vous attendez (c'est-à-dire ne pas exploser). Cela se casse si votre classe, ou toute classe que votre classe contient, a comme membre un pointeur ou une référence ou une classe qui a une fonction virtuelle. Essentiellement, si des pointeurs doivent être impliqués quelque part, ce ne sont pas des données anciennes simples.


6
Les pointeurs sont autorisés dans les structures POD. Les références ne le sont pas.
j_random_hacker

1
Passant manque ici.
icbytes
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.