Я кілька разів стикався з цим терміном типу POD.
Що це означає?
Я кілька разів стикався з цим терміном типу POD.
Що це означає?
Відповіді:
POD позначає Plain Old Data - тобто клас (визначений ключовим словом struct
чи ключовим словом class
) без конструкторів, деструкторів та функцій віртуальних членів. Стаття Вікіпедії про ПОД детальніше і детальніше визначає її як:
Звичайна стара структура даних в C ++ - це сукупний клас, який містить лише PODS як членів, не має визначеного користувачем деструктора, не визначеного користувачем оператора призначення копії та нестатичних членів типу вказівника на член.
Більш детальну інформацію можна знайти у цій відповіді на C ++ 98/03 . C ++ 11 змінив правила навколо ПДД, сильно розслабивши їх, тим самим вимагаючи подальшої відповіді тут .
POD - це тип (включаючи класи), де компілятор C ++ гарантує, що в структурі не буде ніякої "магії": наприклад, приховані покажчики на vtables, компенсації, які застосовуються до адреси при передачі на інші типи ( принаймні, якщо ПДД цілі теж), конструктори або деструктори. Грубо кажучи, тип - це POD, коли єдиними речами в ньому є вбудовані типи та їх комбінації. Результат - це щось, що "діє як" тип C.
int
, char
, wchar_t
, bool
, float
, double
Є стручки, так само як long/short
і signed/unsigned
їх версій.enums
є ПОДconst
або volatile
POD - це POD.class
, struct
або union
PODs - це POD, за умови, що всі нестатичні члени даних є public
, і він не має базового класу і не має конструкторів, деструкторів або віртуальних методів. Статичні члени не припиняють щось бути ПОД за цим правилом. Це правило змінилося в C ++ 11, і деякі приватні члени дозволені: Чи може клас із усіма приватними членами бути класом POD?3.9 (10): "Арифметичні типи (3.9.1), типи перерахування, типи вказівників та вказівники на типи членів (3.9.2) та версії цих типів (3.9.3), що мають кваліфікацію cv, - це колективно скалярні типи скалярних типів. типи, типи POD-структура, типи підключення POD (п. 9), масиви таких типів і CV-кваліфіковані версії цих типів (3.9.3) разом називаються типами POD
9 (4): "POD-структура - це сукупний клас, який не має нестатичних членів даних типу non-POD-структура, не-POD-об'єднання (або масив таких типів) або посилання, і не має користувача- визначте оператор копіювання і не визначений користувачем деструктор. Подібним чином, підключення POD - це сукупний об'єднання, що не має нестатичних членів даних типу не-POD-структура, не-POD-об'єднання (або масив таких типів) або посилання, і не має визначеного користувачем оператора копіювання і не визначеного користувачем деструктора.
8.5.1 (1): "Сукупність - це масив або клас (п. 9) без оголошених користувачем конструкторів (12.1), без приватних або захищених нестатичних членів даних (п. 11), без базових класів (п. 10) і немає віртуальних функцій (10.3). "
Коротше кажучи, це все вбудовані типи даних (наприклад int
, char
, float
, long
, unsigned char
, double
і т.д.) і все агрегування даних POD. Так, це рекурсивне визначення. ;)
Щоб було зрозуміліше, ПОД - це те, що ми називаємо "структура": одиниця або група одиниць, які просто зберігають дані.
Як я розумію, POD (PlainOldData) - це лише необроблені дані - йому не потрібно:
Як перевірити, чи щось є ПОД? Що ж, існує структура, яка називається 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)>
{ };
}
(З заголовка type_traits)
Довідка:
Об'єкт POD (звичайні старі дані) має один із цих типів даних - фундаментальний тип, вказівник, об'єднання, структура, масив або клас - без конструктора. І навпаки, не-POD об'єкт - це той, для якого існує конструктор. Об'єкт POD починає свій термін експлуатації, коли він отримує сховище відповідного розміру для свого типу, і його термін експлуатації закінчується, коли сховище для об'єкта або повторно використовується або переміщується.
Типи PlainOldData також не повинні мати жодного з:
Більш низьке визначення PlainOldData включає об'єкти з конструкторами; але виключає ті, у кого віртуальне що завгодно. Важливою проблемою для типів PlainOldData є те, що вони не є поліморфними. Спадкування можна проводити з типами POD, однак це слід робити лише для ImplementationInheritance (повторне використання коду), а не поліморфізм / підтипізація.
Поширене (хоча і не зовсім правильне) визначення полягає в тому, що тип PlainOldData - це все, що не має VeeTable.
Чому нам взагалі потрібно розмежовувати POD-та та не-POD?
C ++ розпочав своє життя як розширення C. Хоча сучасний C ++ вже не є суворим набором C, люди все ще очікують високого рівня сумісності між ними.
Грубо кажучи, тип POD - це тип, сумісний з C і, можливо, не менш важливо сумісний з певними оптимізаціями ABI.
Щоб бути сумісним з C, ми повинні задовольнити два обмеження.
Деякі функції C ++ несумісні з цим.
Віртуальні методи вимагають від компілятора вставити один або декілька покажчиків у таблиці віртуальних методів, чого не існує у C.
Визначені користувачем конструктори копій, конструктори переміщення, призначення копій та деструктори впливають на проходження та повернення параметрів. Багато C ABI передають і повертають невеликі параметри в регістри, але посилання, передані призначеному користувачем конструктору / призначенню / деструктору, можуть працювати лише з місцями пам'яті.
Тому необхідно визначити, які типи можна очікувати на "сумісність C", а які - не. C ++ 03 був дещо надто суворим у цьому відношенні, будь-який визначений користувачем конструктор відключив б вбудовані конструктори, і будь-яка спроба додати їх назад призведе до того, що вони будуть визначені користувачем, а отже, і тип не-pod. C ++ 11 відкрив речі досить небагато, дозволивши користувачеві знову представити вбудовані конструктори.
Приклади всіх випадків, що не static_assert
стосуються POD, з ефектами від C ++ 11 до C ++ 17 та POD
std::is_pod
було додано в C ++ 11, тому давайте розглянемо цей стандарт далі.
std::is_pod
буде видалено з C ++ 20, як згадується на веб- сайті https://stackoverflow.com/a/48435532/895245 , оновимо це, коли надійде підтримка для заміни.
Блокування обмежень POD стає все більш невимушеним у міру розвитку стандарту, я прагну висвітлити всі розслаблення у прикладі через ifdefs.
libstdc ++ має тестування на: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc, але це просто занадто мало. Обслуговувачі: з’єднайте це, якщо прочитаєте цю публікацію. Мені лінь перевірити всі проекти проектів C ++, зазначені на веб- сайті: /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
}
Тестували:
for std in 11 14 17; do echo $std; g++-8 -Wall -Werror -Wextra -pedantic -std=c++$std pod.cpp; done
на Ubuntu 18.04, GCC 8.2.0.
Поняття POD та типізований тип std::is_pod
буде застарілим у C ++ 20. Див. Це запитання для отримання додаткової інформації.
З допомогою C ++ "Звичайні старі дані" не означають, що такі речі, як int, char і т. Д., - єдині використовувані типи. Звичайні старі дані насправді означають, що ви можете перенести структуру memcpy з одного місця в пам'яті до іншого, і все буде працювати точно так, як ви очікували (тобто не підірвати). Це руйнується, якщо ваш клас або будь-який клас, який містить ваш клас, як член є вказівником або посиланням або класом, який має віртуальну функцію. По суті, якщо покажчики мають бути десь задіяні, це не звичайні старі дані.