Quali sono i tipi di POD in C ++?


978

Mi sono imbattuto in questo termine di tipo POD alcune volte.
Cosa significa?



5
si prega di consultare chat.stackoverflow.com/transcript/message/213026#213026 e i messaggi del giorno seguente per la discussione sulla risposta accettata
Johannes Schaub - litb


@ paxos1977: cambia la tua selezione di "soluzione" (attualmente la risposta di Hewgill) in modo che una risposta fondamentalmente errata non induca in errore i googler che finiscono qui.
Saluti e hth. - Alf,

Abbiamo concluso che una stringa di tipo c NON è un tipo POD perché 1.) il puntatore non è contiguo ai dati della stringa e 2.) per rendere una stringa un tipo POD, è necessario assicurarsi che il tipo aveva un carattere a termine nullo all'interno della dimensione predefinita del tipo POD, portando a un comportamento indefinito.

Risposte:


695

POD sta per Plain Old Data , ovvero una classe (definita con la parola chiave structo la parola chiave class) senza funzioni di costruttori, distruttori e membri virtuali. L'articolo di Wikipedia su POD è un po 'più dettagliato e lo definisce come:

Una semplice struttura dati vecchia in C ++ è una classe aggregata che contiene solo PODS come membri, non ha distruttori definiti dall'utente, operatori di assegnazione di copie definiti dall'utente e membri non statici di tipo puntatore a membro.

Maggiori dettagli sono disponibili in questa risposta per C ++ 98/03 . C ++ 11 ha cambiato le regole che circondano il POD, rilassandole notevolmente, rendendo quindi necessaria una risposta di seguito .


35
C'è una differenza I tipi intrinseci sono le primitive del linguaggio "incorporato". Questi sono i tipi di POD, oltre a aggregazioni di questi (e altri POD).
Adam Wright,

59
I tipi POD hanno caratteristiche che non i tipi non POD. Ad esempio, se si dispone di una struttura di tipo POD globale, const, è possibile inizializzare il suo contenuto con notazione di parentesi graffa, viene messo in memoria di sola lettura e non è necessario generare alcun codice per inizializzarlo (costruttore o altro), perché fa parte dell'immagine del programma. Questo è importante per le persone embedded che spesso hanno vincoli stretti su RAM, ROM o Flash.
Mike DeSimone,

35
In C ++ 11, puoi fare std :: is_pod <MyType> () per dire se MyType è POD.
codice alleato

7
Il rapporto tecnico di Bjarne Stroustrup sulle prestazioni C ++ afferma che lo standard C ++ descrive un POD come " un tipo di dati compatibile con il tipo di dati equivalente in C nel layout, inizializzazione e capacità di essere copiato con memcpy ". Forse si dovrebbe fare una distinzione tra un tipo POD e una struttura POD.
user34660

6
−1 Questa risposta è ancora fondamentalmente sbagliata e fuorviante al 16 agosto 2016: i tipi POD non sono limitati ai tipi di classe.
Saluti e hth. - Alf,

353

Molto informalmente:

Un POD è un tipo (comprese le classi) in cui il compilatore C ++ garantisce che non ci sarà "magia" nella struttura: ad esempio puntatori nascosti su vtables, offset che vengono applicati all'indirizzo quando viene trasmesso ad altri tipi ( almeno se anche il POD del bersaglio), i costruttori o i distruttori. In parole povere, un tipo è un POD quando le uniche cose in esso contenute sono tipi e combinazioni incorporati. Il risultato è qualcosa che "si comporta come" un tipo C.

Meno informalmente:

  • int, char, wchar_t, bool, float, doubleSono i POD, come lo sono long/shorte signed/unsignedversioni di loro.
  • i puntatori (compresi puntatore a funzione e puntatore a membro) sono POD,
  • enums sono POD
  • a consto volatilePOD è un POD.
  • a class, structo uniondi POD è un POD a condizione che lo siano tutti i membri di dati non statici publice non ha classe di base né costruttori, distruttori o metodi virtuali. I membri statici non impediscono che qualcosa sia un POD in base a questa regola. Questa regola è cambiata in C ++ 11 e alcuni membri privati ​​sono ammessi: una classe con tutti i membri privati ​​può essere una classe POD?
  • Wikipedia ha torto nel dire che un POD non può avere membri di tipo puntatore a membro. O meglio, è corretto per la dicitura C ++ 98, ma TC1 ha reso esplicito che i puntatori a membro sono POD.

Formalmente (standard C ++ 03):

3.9 (10): "I tipi aritmetici (3.9.1), i tipi di enumerazione, i tipi di puntatore e i puntatori ai tipi di membri (3.9.2) e le versioni qualificate per cv di questi tipi (3.9.3) sono collettivamente tipi di chiamanti scalari. tipi, tipi di struttura POD, tipi di unione POD (clausola 9), array di tali tipi e versioni qualificate cv di questi tipi (3.9.3) sono collettivamente chiamati tipi POD "

9 (4): "Una POD-struct è una classe aggregata che non ha membri di dati non statici di tipo non-POD-struct, non-POD-union (o array di tali tipi) o riferimento e non ha user- definire l'operatore di copia e nessun distruttore definito dall'utente. Allo stesso modo un'unione POD è un'unione aggregata che non ha membri di dati non statici di tipo struttura non POD, unione non POD (o array di tali tipi) o riferimento, e non ha un operatore di copia definito dall'utente e nessun distruttore definito dall'utente.

8.5.1 (1): "Un aggregato è un array o una classe (clausola 9) senza costruttori dichiarati dall'utente (12.1), senza membri di dati non statici privati ​​o protetti (clausola 11), senza classi di base (clausola 10) e nessuna funzione virtuale (10.3). "


3
Hai formale / meno formale. È possibile aggiungere una regola empirica. Tipi incorporati e aggregazioni di tipi incorporati (o qualcosa del genere). Inoltre per ottenere la definizione esatta dobbiamo rendere le conoscenze facili da usare.
Martin York,

1
Ti sbagli un po 'sul bit "offset quando cast_to un altro tipo". Tali offset vengono applicati quando si esegue il casting in una classe base o derivata. Pertanto, se si esegue il cast da un puntatore di classe di base POD a una classe non POD derivata, è comunque possibile riscontrare un aggiustamento.
MSalters

1
@Steve Jessop: Perché dobbiamo distinguere tra POD e non POD?
Lazer

6
@Lazer: è un'altra domanda, "come si comportano i POD?" al contrario di "cosa significa POD?". In sintesi, la differenza riguarda l'inizializzazione (quindi anche l'uso di memcpy per duplicare oggetti), la compatibilità con il layout C struct per quel compilatore e il puntatore su e giù. I POD "agiscono come i tipi C", i non POD non sono garantiti. Quindi, se vuoi che il tuo tipo si comporti in modo portabile come una struttura C, devi assicurarti che sia POD, quindi devi conoscere la differenza.
Steve Jessop,

4
@muntoo: è stato, in realtà stavo commentando la risposta che cita informazioni obsolete da Wikipedia. Potrei modificare quella risposta, suppongo, ma sento l'odore dei problemi se vado in giro a modificare la risposta di altre persone per essere d'accordo con la mia, non importa quanto io abbia ragione.
Steve Jessop,

21

Vecchi dati semplici

In breve, sono tutti i tipi di dati integrati (ad es int.char , float, long, unsigned char, double, ecc) e tutti aggregazione dei dati POD. Sì, è una definizione ricorsiva. ;)

Per essere più chiari, un POD è ciò che chiamiamo "una struttura": un'unità o un gruppo di unità che memorizzano solo i dati.


13
È vero che a volte li chiamiamo "una struttura". Tuttavia, abbiamo sempre torto a farlo, dal momento che una struttura non è necessariamente un tipo POD.
Steve Jessop,

7
ovviamente ... struct e class sono quasi equivalenti, ma in "the business" chiamiamo "a struct" un semplice raccoglitore di dati, di solito senza agenti e agenti, di solito con semantica di valore ...
ugasoft,

2
Per me è stato C ++ sbagliato a rendere struct identica alla parola chiave class o vicina a: struct aggiunge solo l'accesso pubblico predefinito alla classe. Ero più semplice creare strutture simili a C e avremmo avuto i POD il giorno 0 di c ++.
user1708042

ugasoft: la tua risposta potrebbe essere fuorviante - il tuo commento ha spiegato il dettaglio mancante che viene utilizzato in questo modo in pratica, piuttosto che standard. Whoa, 8 anni, sei anche qui? ;-)
hauron

Ad eccezione di una stringa perché non è possibile copiarla con memcpy senza prima determinare la lunghezza della stringa.

12

Da quanto ho capito, POD (PlainOldData) è solo un dato grezzo, non ha bisogno di:

  • da costruire,
  • essere distrutto,
  • avere operatori personalizzati.
  • Non deve avere funzioni virtuali,
  • e non deve ignorare gli operatori.

Come verificare se qualcosa è un POD? Bene, c'è una struttura per quella chiamata 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)>
  { };
}

(Dall'intestazione type_traits)


Riferimento:


2
Non corretto, un tipo POD può avere funzioni membro o operatori sovraccarichi. (Ma potrebbe non avere funzioni membro virtuale.)
Colin D Bennett,

@ColinDBennett Sì, è vero. Dispiace per la confusione. Modificato dentro / fuori dalla risposta.
набиячлэвэли

10

Un oggetto POD (semplici vecchi dati) ha uno di questi tipi di dati - un tipo fondamentale, puntatore, unione, struttura, matrice o classe - senza costruttore. Al contrario, un oggetto non POD è uno per il quale esiste un costruttore. Un oggetto POD inizia la sua durata quando ottiene memoria con le dimensioni appropriate per il suo tipo e la sua durata termina quando la memoria per l'oggetto viene riutilizzata o deallocata.

Anche i tipi PlainOldData non devono avere nessuno dei seguenti:

  • Funzioni virtuali (proprie o ereditate)
  • Classi di base virtuali (dirette o indirette).

Una definizione più libera di PlainOldData include oggetti con costruttori; ma esclude quelli con qualsiasi cosa virtuale. Il problema importante con i tipi PlainOldData è che non sono polimorfici. L'ereditarietà può essere eseguita con tipi di POD, tuttavia dovrebbe essere eseguita solo per ImplementationInheritance (riutilizzo del codice) e non polimorfismo / sottotipizzazione.

Una definizione comune (anche se non strettamente corretta) è che un tipo PlainOldData è tutto ciò che non ha una VeeTable.


La tua risposta è molto buona, ma questa domanda ha accettato la risposta 8 anni fa, oltre a molte altre buone risposte. Puoi contribuire di più a SO se usi le tue conoscenze per rispondere a domande a cui non è stata ancora data risposta)))
mvidelgauz

10

Perché dobbiamo distinguere tra POD e non POD?

Il C ++ ha iniziato la sua vita come estensione di C. Mentre il C ++ moderno non è più un superset rigoroso di C, le persone si aspettano ancora un alto livello di compatibilità tra i due.

In parole povere, un tipo POD è un tipo compatibile con C e forse altrettanto importante è compatibile con alcune ottimizzazioni ABI.

Per essere compatibili con C, dobbiamo soddisfare due vincoli.

  1. Il layout deve essere uguale al tipo C corrispondente.
  2. Il tipo deve essere passato e restituito dalle funzioni allo stesso modo del tipo C corrispondente.

Alcune funzionalità C ++ non sono compatibili con questa.

I metodi virtuali richiedono che il compilatore inserisca uno o più puntatori nelle tabelle dei metodi virtuali, qualcosa che non esiste in C.

Costruttori di copie definiti dall'utente, costruttori di spostamento, assegnazioni di copia e distruttori hanno implicazioni per il passaggio e la restituzione dei parametri. Molti ABI C passano e restituiscono piccoli parametri nei registri, ma i riferimenti passati al costruttore / assigmento / distruttore definito dall'utente possono funzionare solo con posizioni di memoria.

Quindi è necessario definire quali tipi ci si può aspettare che siano "C compatibili" e quali tipi non possono esserlo. C ++ 03 è stato in qualche modo troppo severo in questo senso, qualsiasi costruttore definito dall'utente disabiliterebbe i costruttori integrati e qualsiasi tentativo di aggiungerli di nuovo comporterebbe che fossero definiti dall'utente e quindi il tipo non fosse pod. C ++ 11 ha aperto un po 'le cose, consentendo all'utente di reintrodurre i costruttori integrati.


8

Esempi di tutti i casi non POD con static_asserteffetti da C ++ 11 a C ++ 17 e POD

std::is_pod è stato aggiunto in C ++ 11, quindi consideriamo quello standard per ora.

std::is_podverrà rimosso da C ++ 20 come indicato in https://stackoverflow.com/a/48435532/895245 , aggiorniamo questo quando arriva il supporto per le sostituzioni.

Le restrizioni del POD sono diventate sempre più rilassate man mano che lo standard si è evoluto, mirando a coprire tutti i rilassamenti nell'esempio attraverso ifdefs.

libstdc ++ ha un po 'di test su: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc ma troppo poco. Manutentori: si prega di unire questo se leggi questo post. Sono pigro a dare un'occhiata a tutti i progetti di testuite C ++ menzionati su: /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 a monte .

Testato con:

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

su Ubuntu 18.04, GCC 8.2.0.


4

Il concetto di POD e la caratteristica del tipo std::is_podsaranno deprecati in C ++ 20. Vedi questa domanda per ulteriori informazioni.


-7

Con C ++, Plain Old Data non significa solo che cose come int, char, ecc. Sono gli unici tipi utilizzati. In pratica, Old Old Data significa in pratica che puoi prendere una struttura memcpy da una posizione in memoria a un'altra e le cose funzioneranno esattamente come ti aspetteresti (cioè non far saltare in aria). Questo si interrompe se la tua classe, o qualsiasi classe contenuta nella tua classe, ha come membro un puntatore o un riferimento o una classe che ha una funzione virtuale. In sostanza, se i puntatori devono essere coinvolti da qualche parte, non si tratta di semplici vecchi dati.


6
I puntatori sono consentiti nelle strutture POD. I riferimenti non lo sono.
j_random_hacker,

1
Qui manca il passante.
Icbytes,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.