Я встречал этот термин POD-type несколько раз.
Что это значит?
Я встречал этот термин POD-type несколько раз.
Что это значит?
Ответы:
POD означает « Обычные старые данные», то есть класс (определяемый с помощью ключевого слова struct
или ключевого слова class
) без конструкторов, деструкторов и функций виртуальных членов. Статья Википедии о POD углубляется в детали и определяет ее как:
Простая старая структура данных в C ++ - это агрегатный класс, который содержит только PODS в качестве членов, не имеет пользовательского деструктора, пользовательского оператора копирования и нестатических членов типа указатель на член.
Более подробную информацию можно найти в этом ответе для C ++ 98/03 . C ++ 11 изменил правила, связанные с POD, значительно ослабив их, что потребовало здесь последующего ответа .
POD - это тип (включая классы), в котором компилятор C ++ гарантирует, что в структуре не будет «магии»: например, скрытые указатели на vtables, смещения, которые применяются к адресу при его приведении к другим типам ( по крайней мере, если POD цели тоже), конструкторы или деструкторы. Грубо говоря, тип - это POD, когда в нем есть только встроенные типы и их комбинации. Результатом является то, что «действует как» тип Си.
int
, char
, wchar_t
, bool
, float
, double
Являются стручки, так же как long/short
и signed/unsigned
их версий.enums
это стручкиconst
или volatile
POD является POD.class
, struct
или union
POD - это POD при условии, что все нестатические члены-данные есть public
, и он не имеет базового класса и не имеет конструкторов, деструкторов или виртуальных методов. Статические члены не перестают быть POD согласно этому правилу. Это правило изменилось в C ++ 11, и некоторые закрытые члены допускаются: может ли класс со всеми закрытыми членами быть классом POD?3.9 (10): «Арифметические типы (3.9.1), типы перечисления, типы указателей и указатели на типы элементов (3.9.2) и cv-квалифицированные версии этих типов (3.9.3) являются совместно вызываемыми скалярными типами. Скалярные типы, типы POD-структуры, типы POD-объединения (раздел 9), массивы таких типов и cv-квалифицированные версии этих типов (3.9.3) вместе называются типами POD "
9 (4): «POD-структура - это агрегатный класс, который не имеет нестатических членов-данных типа non-POD-struct, non-POD-union (или массива таких типов) или ссылки, и не имеет пользовательских определить оператор копирования и не определяемый пользователем деструктор. Аналогично POD-объединение - это агрегатное объединение, которое не имеет нестатических членов-данных типа non-POD-struct, non-POD-union (или массива таких типов) или ссылки, и не имеет пользовательского оператора копирования и пользовательского деструктора.
8.5.1 (1): «Агрегат - это массив или класс (раздел 9) без объявленных пользователем конструкторов (12.1), без закрытых или защищенных нестатических элементов данных (раздел 11), без базовых классов (раздел 10) и никаких виртуальных функций (10.3). "
Короче говоря, это все встроенные типы данных (например int
, char
, float
, long
, unsigned char
, double
и т.д.) и все агрегирование данных POD. Да, это рекурсивное определение. ;)
Чтобы быть более понятным, POD - это то, что мы называем «структурой»: блок или группа блоков, которые просто хранят данные.
Как я понимаю, POD (PlainOldData) - это просто необработанные данные - они не нужны:
Как проверить, является ли что-то POD? Ну, есть структура для этого называется 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, однако это должно быть сделано только для реализацииInheritance (повторное использование кода), а не полиморфизма / подтипирования.
Распространенное (хотя и не совсем правильное) определение состоит в том, что тип PlainOldData - это все, что не имеет VeeTable.
Почему мы должны различать POD и не POD вообще?
C ++ начал свою жизнь как расширение C. Хотя современный C ++ больше не является строгим надмножеством C, люди все еще ожидают высокого уровня совместимости между ними.
Грубо говоря, POD-тип - это тип, который совместим с C и, что не менее важно, совместим с некоторыми оптимизациями ABI.
Чтобы быть совместимым с C, нам нужно выполнить два ограничения.
Некоторые функции C ++ несовместимы с этим.
Виртуальные методы требуют, чтобы компилятор вставлял один или несколько указателей на таблицы виртуальных методов, чего нет в C.
Определенные пользователем конструкторы копирования, конструкторы перемещения, назначения копирования и деструкторы имеют значение для передачи и возврата параметров. Многие C ABI передают и возвращают небольшие параметры в регистрах, но ссылки, передаваемые пользовательскому конструктору / ассигменту / деструктору, могут работать только с ячейками памяти.
Поэтому необходимо определить, какие типы могут быть «совместимыми с C», а какие нет. C ++ 03 был несколько строгим в этом отношении, любой определяемый пользователем конструктор отключал бы встроенные конструкторы, и любая попытка добавить их обратно приводила бы к тому, что они определялись пользователем, и, следовательно, тип не являлся pod. C ++ 11 довольно многое открыл для себя, позволив пользователю заново представить встроенные конструкторы.
Примеры всех случаев без POD с static_assert
эффектами от 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 ++ testsuite, упомянутые по адресу: /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 ++ Plain Old Data не просто означает, что такие типы, как int, char и т. Д., Являются единственными используемыми типами. Обычные старые данные на практике означают, что вы можете перенести struct memcpy из одного места в памяти в другое, и все будет работать так, как вы ожидаете (то есть не взорваться). Это нарушается, если ваш класс или любой класс, содержащийся в вашем классе, имеет в качестве члена указатель, ссылку или класс, имеющий виртуальную функцию. По сути, если указатели должны быть где-то задействованы, это не Plain Old Data.