Der Begriff POD-Typ ist mir schon ein paar Mal begegnet.
Was bedeutet das?
- Was sind Aggregate und PODs und warum sind sie so besonders? (5 Antworten )
Antworten
Zu viele Anzeigen?POD steht für Normale Daten - d. h. eine Klasse (unabhängig davon, ob sie mit dem Schlüsselwort struct
oder das Schlüsselwort class
) ohne Konstruktoren, Destruktoren und virtuelle Mitgliederfunktionen. Der Wikipedia-Artikel über POD geht ein wenig mehr ins Detail und definiert es als:
Eine Plain Old Data Structure in C++ ist eine Aggregatklasse, die nur PODS als Mitglieder enthält, keinen benutzerdefinierten Destruktor, keinen benutzerdefinierten Kopierzuweisungsoperator und keine nichtstatischen Mitglieder vom Typ Pointer-to-Member hat.
Ausführlichere Informationen finden Sie in diese Antwort für C++98/03 . C++11 änderte die Regeln für POD und lockerte sie erheblich, so dass was eine weitere Antwort hier erforderlich macht .
Sehr informell:
Ein POD ist ein Typ (einschließlich Klassen), bei dem der C++-Compiler garantiert, dass in der Struktur keine "Magie" stattfindet: z. B. versteckte Zeiger auf vtables, Offsets, die auf die Adresse angewendet werden, wenn sie in andere Typen gecastet wird (zumindest, wenn das Ziel ebenfalls ein POD ist), Konstruktoren oder Destruktoren. Grob gesagt ist ein Typ ein POD, wenn die einzigen Dinge in ihm eingebaute Typen und Kombinationen davon sind. Das Ergebnis ist etwas, das sich "wie" ein C-Typ verhält.
Weniger informell:
int
,char
,wchar_t
,bool
,float
,double
sind PODs, ebenso wielong/short
ysigned/unsigned
Versionen von ihnen.- Zeiger (einschließlich Pointer-to-Function und Pointer-to-Member) sind PODs,
enums
sind PODs- a
const
ovolatile
Ein POD ist ein POD. - a
class
,struct
ounion
von PODs ist ein POD, sofern alle nicht-statischen Datenelementepublic
und hat keine Basisklasse und keine Konstruktoren, Destruktoren oder virtuellen Methoden. Statische Mitglieder verhindern nicht, dass etwas nach dieser Regel ein POD ist. Diese Regel hat sich in C++11 geändert und bestimmte private Mitglieder sind erlaubt: Kann eine Klasse mit allen privaten Mitgliedern eine POD-Klasse sein? - Die Aussage von Wikipedia, dass ein POD keine Mitglieder vom Typ Zeiger-auf-Glied haben kann, ist falsch. Oder besser gesagt, es ist korrekt für den Wortlaut von C++98, aber TC1 hat ausdrücklich festgelegt, dass Zeiger-auf-Glied POD sind.
Formal (C++03 Standard):
3.9(10): "Arithmetische Typen (3.9.1), Aufzählungstypen, Zeigertypen und Zeiger auf Membertypen (3.9.2) sowie cv-qualifizierte Versionen dieser Typen (3.9.3) sind zusammenfassend als skalare Typen bezeichnet. Skalartypen, POD-Strukturtypen, POD-Union-Typen (Abschnitt 9), Arrays solcher Typen und cv-qualifizierte Versionen dieser Typen (3.9.3) werden zusammen als POD-Typen bezeichnet.
9(4): "Ein POD-struct ist eine aggregierte Klasse, die keine nichtstatischen Datenelemente vom Typ non-POD-struct, non-POD-union (oder Array solcher Typen) oder Referenz hat, und die keinen benutzerdefinierten Kopieroperator und keinen benutzerdefinierten Destruktor hat. Analog dazu ist eine POD-Union eine aggregierte Union, die keine nichtstatischen Datenelemente vom Typ Nicht-POD-Struktur, Nicht-POD-Union (oder Array solcher Typen) oder Referenz hat und keinen benutzerdefinierten Kopieroperator und keinen benutzerdefinierten Destruktor hat.
8.5.1(1): "Ein Aggregat ist ein Array oder eine Klasse (Abschnitt 9) ohne benutzerdeklarierte Konstruktoren (12.1), ohne private oder geschützte nichtstatische Datenmitglieder (Abschnitt 11), ohne Basisklassen (Abschnitt 10) und ohne virtuelle Funktionen (10.3)."
Kurz gesagt handelt es sich um alle eingebauten Datentypen (z. B. int
, char
, float
, long
, unsigned char
, double
usw.) und alle Aggregationen von POD-Daten. Ja, es ist eine rekursive Definition ;)
Ein POD ist das, was wir eine "Struktur" nennen: eine Einheit oder eine Gruppe von Einheiten, die lediglich Daten speichern.
Warum müssen wir überhaupt zwischen POD's und Nicht-POD's differenzieren?
C++ begann sein Leben als eine Erweiterung von C. Obwohl modernes C++ nicht mehr eine strenge Obermenge von C ist, wird immer noch ein hohes Maß an Kompatibilität zwischen den beiden erwartet. Die "C ABI" einer Plattform dient häufig auch als De-facto-Standard-ABI für andere Sprachen auf dieser Plattform.
Grob gesagt, ist ein POD-Typ ein Typ, der mit C kompatibel ist und, was vielleicht ebenso wichtig ist, mit bestimmten ABI-Optimierungen kompatibel ist.
Um mit C kompatibel zu sein, müssen wir zwei Bedingungen erfüllen.
- Das Layout muss mit dem entsprechenden C-Typ übereinstimmen.
- Der Typ muss in gleicher Weise wie der entsprechende C-Typ an Funktionen übergeben und von diesen zurückgegeben werden.
Bestimmte C++-Funktionen sind damit nicht kompatibel.
Virtuelle Methoden erfordern, dass der Compiler einen oder mehrere Zeiger auf virtuelle Methodentabellen einfügt, etwas, das es in C nicht gibt.
Benutzerdefinierte Copy-Konstruktoren, Move-Konstruktoren, Copy-Zuweisungen und Destruktoren haben Auswirkungen auf die Parameterübergabe und -rückgabe. Viele C ABIs übergeben und geben kleine Parameter in Registern zurück, aber die Referenzen, die an den benutzerdefinierten Konstruktor/Zuweisung/Destruktor übergeben werden, können nur mit Speicherplätzen arbeiten.
Es muss also definiert werden, welche Typen als "C-kompatibel" gelten können und welche nicht. C++03 war in dieser Hinsicht etwas zu streng: Jeder benutzerdefinierte Konstruktor würde die eingebauten Konstruktoren deaktivieren, und jeder Versuch, sie wieder hinzuzufügen, würde dazu führen, dass sie benutzerdefiniert sind und der Typ daher nicht kompatibel ist. C++11 öffnete die Dinge ein wenig, indem es dem Benutzer erlaubte, die eingebauten Konstruktoren wieder einzuführen.
Beispiele für alle Nicht-POD-Fälle mit static_assert
von C++11 zu C++17 und POD-Effekte
std::is_pod
wurde in C++11 hinzugefügt, also betrachten wir diesen Standard erst einmal weiter.
std::is_pod
wird aus C++20 entfernt werden, wie unter https://stackoverflow.com/a/48435532/895245 Wir werden dies aktualisieren, sobald die Unterstützung für die Ersatzgeräte eintrifft.
Die POD-Beschränkungen wurden im Laufe der Entwicklung des Standards immer mehr gelockert, ich versuche, alle Lockerungen im Beispiel durch ifdefs abzudecken.
libstdc++ hat ein winziges Stückchen Test bei: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc aber es ist einfach zu wenig. Maintainer: Bitte fügen Sie dies zusammen, wenn Sie diesen Beitrag lesen. Ich bin zu faul, um alle C++-Testsuite-Projekte zu überprüfen, die auf erwähnt werden: 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
}
Getestet mit:
for std in 11 14 17; do echo $std; g++-8 -Wall -Werror -Wextra -pedantic -std=c++$std pod.cpp; done
auf Ubuntu 18.04, GCC 8.2.0.
- See previous answers
- Weitere Antworten anzeigen