Kilka razy spotkałem się z tym terminem.
Co to znaczy?
Kilka razy spotkałem się z tym terminem.
Co to znaczy?
Odpowiedzi:
POD oznacza Plain Old Data - czyli klasę (zdefiniowaną za pomocą słowa kluczowego struct
lub słowa kluczowego class
) bez funkcji konstruktorów, destruktorów i elementów wirtualnych. Artykuł Wikipedii na temat POD jest nieco bardziej szczegółowy i definiuje go jako:
Zwykła stara struktura danych w C ++ to klasa agregująca, która zawiera tylko elementy PODS jako elementy, nie ma niszczyciela zdefiniowanego przez użytkownika, żadnego operatora przypisania kopii zdefiniowanego przez użytkownika ani żadnych niestatycznych elementów typu wskaźnik do elementu.
Więcej szczegółów można znaleźć w tej odpowiedzi dla C ++ 98/03 . C ++ 11 zmienił zasady dotyczące POD, znacznie je rozluźniając, wymagając w związku z tym odpowiedzi uzupełniającej .
POD jest typem (w tym klasami), w którym kompilator C ++ gwarantuje, że w strukturze nie będzie „magii”: na przykład ukryte wskaźniki do tabel vt, przesunięcia, które są stosowane do adresu, gdy jest rzutowany na inne typy ( przynajmniej jeśli POD jest również celem), konstruktorów lub destruktorów. Z grubsza mówiąc, typ jest POD, gdy jedyne w nim są wbudowane typy i ich kombinacje. Rezultatem jest coś, co „działa jak” typ C.
int
, char
, wchar_t
, bool
, float
, double
Są POD, podobnie jak long/short
i signed/unsigned
wersje nich.enums
są PODconst
lub volatile
POD jest POD.class
, struct
lub union
POD jest POD, pod warunkiem, że wszystkie niestatyczne elementy danych są public
, i nie ma żadnej klasy bazowej ani żadnych konstruktorów, destruktorów ani metod wirtualnych. Członkowie statyczni nie zatrzymują, aby coś było POD jako podstawa tej zasady. Ta reguła uległa zmianie w C ++ 11 i niektórzy członkowie prywatni są dozwoleni: czy klasa ze wszystkimi członkami prywatnymi może być klasą POD?3.9 (10): „Typy arytmetyczne (3.9.1), typy wyliczeń, typy wskaźników i wskaźnik do typów elementów (3.9.2) i wersje tych typów zakwalifikowane do CV (3.9.3) są zbiorowymi typami skalarów wywołujących. Skalarne typy, typy struktur POD, typy związków POD (klauzula 9), tablice takich typów i wersje tych typów zakwalifikowane do CV (3.9.3) są wspólnie nazywane typami POD ”
9 (4): „Struktura POD jest strukturą zagregowaną, która nie ma elementów danych niestatycznych typu non-POD-struct, non-POD-union (lub tablica takich typów) lub referencji i nie ma użytkownika zdefiniuj operator kopiowania i brak niszczyciela zdefiniowanego przez użytkownika. Podobnie związek POD jest związkiem agregowanym, który nie ma elementów danych niestatycznych typu non-POD-struct, non-POD-union (lub tablica takich typów) lub odniesienia, i nie ma operatora kopiowania zdefiniowanego przez użytkownika ani destruktora zdefiniowanego przez użytkownika.
8.5.1 (1): „Agregacja to tablica lub klasa (klauzula 9) bez konstruktorów zadeklarowanych przez użytkownika (12.1), bez prywatnych lub chronionych niestatycznych elementów danych (klauzula 11), bez klas podstawowych (klauzula 10) i brak funkcji wirtualnych (10.3). ”
W skrócie, to wszystko jest wbudowane typy danych (na przykład int
, char
, float
, long
, unsigned char
, double
, itd.) I wszystkie agregacji danych POD. Tak, to definicja rekurencyjna. ;)
Aby być bardziej zrozumiałym, POD jest tym, co nazywamy „struct”: jednostka lub grupa jednostek, które tylko przechowują dane.
Jak rozumiem POD (PlainOldData) to tylko surowe dane - nie potrzebuje:
Jak sprawdzić, czy coś jest POD? Istnieje struktura, która nazywa się 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)>
{ };
}
(Z nagłówka type_traits)
Odniesienie:
Obiekt POD (zwykłe stare dane) ma jeden z tych typów danych - typ podstawowy, wskaźnik, związek, strukturę, tablicę lub klasę - bez konstruktora. I odwrotnie, obiekt niebędący POD jest obiektem, dla którego istnieje konstruktor. Żywotność obiektu POD rozpoczyna się, gdy uzyskuje miejsce do przechowywania o rozmiarze odpowiednim dla swojego typu, a okres jego użytkowania kończy się, gdy miejsce dla obiektu zostanie ponownie wykorzystane lub zwolnione.
Typy PlainOldData również nie mogą mieć żadnego z:
Luźniejsza definicja PlainOldData obejmuje obiekty z konstruktorami; ale wyklucza osoby posiadające cokolwiek wirtualnego. Ważnym problemem związanym z typami PlainOldData jest to, że nie są one polimorficzne. Dziedziczenie można wykonać z typami POD, jednak należy to zrobić tylko dla ImplementationInheritance (ponowne użycie kodu), a nie polimorfizm / podtyp.
Częstą (choć nie do końca poprawną) definicją jest to, że typ PlainOldData to wszystko, co nie ma VeeTable.
Dlaczego w ogóle musimy rozróżniać POD i POD-POD?
C ++ zaczęło swoje życie jako rozszerzenie C. Chociaż współczesne C ++ nie jest już ścisłym nadzorem C, ludzie nadal oczekują wysokiego poziomu kompatybilności między nimi.
Z grubsza mówiąc, typ POD jest typem kompatybilnym z C i być może równie ważnym jest kompatybilny z niektórymi optymalizacjami ABI.
Aby być kompatybilnym z C, musimy spełnić dwa ograniczenia.
Niektóre funkcje C ++ są z tym niezgodne.
Metody wirtualne wymagają, aby kompilator wstawił jeden lub więcej wskaźników do tabel metod wirtualnych, co nie istnieje w C.
Zdefiniowane przez użytkownika konstruktory kopiowania, konstruktory przenoszenia, przypisania kopii i destruktory mają wpływ na przekazywanie i zwracanie parametrów. Wiele C ABI przekazuje i zwraca małe parametry w rejestrach, ale odniesienia przekazywane do zdefiniowanego przez użytkownika konstruktora / przypisania / destruktora mogą działać tylko z lokalizacjami pamięci.
Konieczne jest więc zdefiniowanie, jakie typy mogą być „kompatybilne z C”, a jakie nie. C ++ 03 był nieco zbyt rygorystyczny pod tym względem, każdy konstruktor zdefiniowany przez użytkownika wyłączyłby wbudowane konstruktory, a każda próba dodania ich z powrotem spowodowałaby, że byłyby zdefiniowane przez użytkownika, a zatem typ nie byłby pod. C ++ 11 całkiem sporo otworzył, umożliwiając użytkownikowi ponowne wprowadzenie wbudowanych konstruktorów.
Przykłady wszystkich przypadków innych niż POD z static_assert
efektami od C ++ 11 do C ++ 17 i POD
std::is_pod
został dodany w C ++ 11, więc na razie rozważmy ten standard.
std::is_pod
zostanie usunięty z C ++ 20, jak wspomniano na https://stackoverflow.com/a/48435532/895245 , zaktualizujmy to, gdy pojawi się wsparcie dla zamienników.
Ograniczenia POD stają się coraz bardziej rozluźnione w miarę rozwoju standardu, staram się objąć wszystkie relaksacje w tym przykładzie poprzez ifdefs.
libstdc ++ ma trochę testów na: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc, ale to po prostu za mało. Opiekunowie: połącz to, jeśli czytasz ten post. Jestem leniwy, by sprawdzić wszystkie projekty testów C ++ wspomniane na stronie : /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
}
Testowane z:
for std in 11 14 17; do echo $std; g++-8 -Wall -Werror -Wextra -pedantic -std=c++$std pod.cpp; done
na Ubuntu 18.04, GCC 8.2.0.
Pojęcie POD i cechy typu std::is_pod
będzie przestarzałe w C ++ 20. Zobacz to pytanie, aby uzyskać dodatkowe informacje.
W C ++ Plain Old Data nie oznacza tylko, że jedynymi używanymi typami są int, char, itp. Zwykłe stare dane naprawdę oznaczają w praktyce, że możesz przenieść struct memcpy z jednej lokalizacji w pamięci do drugiej i wszystko będzie działało dokładnie tak, jak byś się spodziewał (tj. Nie wybuchł). To się psuje, jeśli twoja klasa lub jakakolwiek klasa, którą zawiera twoja klasa, ma jako element członkowski, który jest wskaźnikiem, referencją lub klasą, która ma funkcję wirtualną. Zasadniczo, jeśli wskaźniki muszą być gdzieś zaangażowane, nie są to zwykłe stare dane.