Mi sono imbattuto in questo termine di tipo POD alcune volte.
Cosa significa?
Mi sono imbattuto in questo termine di tipo POD alcune volte.
Cosa significa?
Risposte:
POD sta per Plain Old Data , ovvero una classe (definita con la parola chiave struct
o 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 .
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.
int
, char
, wchar_t
, bool
, float
, double
Sono i POD, come lo sono long/short
e signed/unsigned
versioni di loro.enums
sono PODconst
o volatile
POD è un POD.class
, struct
o union
di POD è un POD a condizione che lo siano tutti i membri di dati non statici public
e 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?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). "
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.
Da quanto ho capito, POD (PlainOldData) è solo un dato grezzo, non ha bisogno di:
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:
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:
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.
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.
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.
Esempi di tutti i casi non POD con static_assert
effetti da C ++ 11 a C ++ 17 e POD
std::is_pod
è stato aggiunto in C ++ 11, quindi consideriamo quello standard per ora.
std::is_pod
verrà 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
}
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.
Il concetto di POD e la caratteristica del tipo std::is_pod
saranno deprecati in C ++ 20. Vedi questa domanda per ulteriori informazioni.
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.