web-dev-qa-db-fra.com

Quels sont les types de POD en C ++?

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

894
paxos1977

POD représente Plain Old Data - c'est-à-dire une classe (qu’il soit défini avec le mot-clé struct ou avec le mot-clé class) sans les fonctions constructeurs, destructeurs et membres virtuels. L'article de Wikipedia sur POD entre un peu plus en détail et le définit comme suit:

Une ancienne structure de données standard en C++ est une classe agrégée qui ne contient que des PODS, ne possède aucun 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 à membre.

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

637
Greg Hewgill

Très informellement:

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 vtables, des décalages qui s’appliquent à l’adresse lorsqu’elle est convertie en d’autres types ( du moins si le POD de la cible aussi), constructeurs ou destructeurs. Grosso modo, un type est un POD lorsque ses seuls éléments sont des types intégrés et des combinaisons de ceux-ci. Le résultat est quelque chose qui "agit comme" un type C.

Moins informellement:

  • int, char, wchar_t, bool, float, double sont des POD, tout comme long/short et signed/unsigned versions d'eux.
  • les pointeurs (y compris pointeur vers fonction et pointeur vers membre) sont des POD,
  • enums sont des POD
  • un const ou volatile POD est un POD.
  • une class, struct ou union des POD est un POD à condition que tous les membres de données non statiques soient public, et qu'il n'ait pas de classe de base ni de constructeurs, de destructeurs, ou des méthodes virtuelles. Les membres statiques n'arrêtent pas que quelque chose soit un POD en vertu de cette règle. Cette règle a changé dans C++ 11 et certains membres privés sont autorisés: ne 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 à membre. Ou plutôt, c'est correct pour le libellé C++ 98, mais TC1 a explicitement expliqué que les pointeurs sur membre sont des POD.

Formellement (norme C++ 03):

3.9 (10): "Types arithmétiques (3.9.1), types d’énumération, types de pointeur et pointeur sur les types de membre (3.9.2) et cv les versions qualifiées de ces types (3.9.3) sont collectivement des types scalaires d’appelant: types scalaires, types POD-struct, types POD-union (article 9), tableaux de ce type et versions valables cv (3.9.3) ) sont collectivement appelés types de POD "

9 (4): "Une structure POD est une classe d'agrégat qui ne possède pas de données membres non statiques de type non-POD-struct, non POD-union (ou un tableau de ce type) ou de référence et ne possède aucun opérateur de copie défini par l'utilisateur ni aucun destructeur défini par l'utilisateur. De même, une union POD est une union d'agrégat qui ne possède aucun membre de données non statique de type non-POD. -struct, non-POD-union (ou un tableau de ce type) ou une référence, et n'a pas d'opérateur de copie défini par l'utilisateur ni de destructeur défini par l'utilisateur.

8.5.1 (1): "Un agrégat est un tableau ou une classe (clause 9) sans constructeur déclaré par l'utilisateur (12.1), ni privé ni données protégées non statiques (clause 11), pas de classes de base (clause 10) ni de fonctions virtuelles (10.3). "

329
Steve Jessop

Plain Old Data

En bref, il s'agit de tous les types de données intégrés (par exemple, int, char, float, long, unsigned char, double, etc. .) et toute l’agrégation de données POD. Oui, c'est une définition récursive. ;)

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

18
ugasoft

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

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

Comment vérifier si quelque chose est un POD? Eh bien, il existe 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)>
  { };
}

(À partir de l'en-tête type_traits)


Référence:

11

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

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 vague de PlainOldData inclut des objets avec des constructeurs; mais exclut ceux qui ont quelque chose de virtuel. Le problème important des types PlainOldData est qu’ils ne sont pas polymorphes. L'héritage peut être effectué avec les types POD, mais ne doit l'être que pour ImplementationInheritance (réutilisation du code) et non pour le polymorphisme/sous-typage.

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

8
amitabes

Exemples de tous les cas autres que POD avec des effets static_assert de C++ 11 à C++ 17 et POD

std::is_pod a été ajouté en C++ 11, considérons donc cette norme pour le moment.

std::is_pod sera supprimé du C++ 20, comme indiqué à l’adresse https://stackoverflow.com/a/48435532/895245 , mettons à jour cette information dès que le support technique arrive pour les remplacements.

Les restrictions de POD sont de plus en plus assouplies au fur et à mesure de l'évolution de la norme, mon objectif est de couvrir toutes les relaxations dans l'exemple via ifdefs.

libstdc ++ a un peu de tests à: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value .cc mais c'est 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 à: https://softwareengineering.stackexchange.com/questions/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
        // https://stackoverflow.com/questions/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.
        // https://stackoverflow.com/questions/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:
            // https://stackoverflow.com/questions/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:
    // https://stackoverflow.com/questions/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
        // https://stackoverflow.com/questions/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.

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

Le C++ a commencé sa vie comme une extension du C. Bien que le C++ moderne ne soit plus un sur-ensemble strict du C, les utilisateurs s'attendent toujours à un haut niveau de compatibilité entre les deux.

Grosso modo, un type de POD est compatible avec le C et peut-être tout aussi important avec certaines optimisations ABI.

Pour être compatible avec C, il faut satisfaire deux contraintes.

  1. La mise en page doit être identique au type C correspondant.
  2. Le type doit être passé et renvoyé des 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 assignations de copie et les destructeurs ont des implications pour le passage et le retour de paramètres. De nombreuses ABI C transmettent et renvoient de petits paramètres dans des registres, mais les références transmises au constructeur/assigment/destructor 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 considérés comme "compatibles C" et quels types ne le peuvent pas. C++ 03 était un peu trop strict à cet égard. C++ 11 a ouvert un peu les choses.

4
plugwash

Le concept de POD et le trait de type std::is_pod seront obsolètes en C++ 20. Voir this question pour plus d'informations.

3
ThomasMcLeod