¿Cuáles son los tipos de POD en C++?
types c++-faq (8)
Muy informalmente:
Un POD es un tipo (incluidas las clases) donde el compilador de C ++ garantiza que no habrá "magia" en la estructura: por ejemplo, punteros ocultos a vtables, compensaciones que se aplican a la dirección cuando se convierte en otros tipos ( al menos si el POD del objetivo también), constructores o destructores. En términos generales, un tipo es un POD cuando las únicas cosas en él son los tipos integrados y sus combinaciones. El resultado es algo que "actúa como" un tipo C.
Menos informalmente:
-
int
,char
,wchar_t
,bool
,float
,double
son PODs, al igual que las versioneslong/short
ysigned/unsigned
de ellos. - los punteros (incluidos puntero a función y puntero a miembro) son POD,
-
enums
son PODs - un POD
const
ovolatile
es un POD. - una
class
,struct
ounion
de POD es un POD siempre que todos los miembros de datos no estáticos seanpublic
y no tenga una clase base ni constructores, destructores o métodos virtuales. Los miembros estáticos no impiden que algo sea un POD según esta regla. Esta regla ha cambiado en C ++ 11 y ciertos miembros privados están permitidos: ¿una clase con todos los miembros privados puede ser una clase POD? - Wikipedia se equivoca al decir que un POD no puede tener miembros de tipo puntero a miembro. O más bien, es correcto para la redacción de C ++ 98, pero TC1 hizo explícito que los punteros a miembros son POD.
Formalmente (Estándar C ++ 03):
3.9 (10): "Los tipos aritméticos (3.9.1), los tipos de enumeración, los tipos de puntero y el puntero a los tipos de miembros (3.9.2) y las versiones calificadas por CV de estos tipos (3.9.3) son colectivamente tipos escalares de llamadores. Los tipos, los tipos de estructura POD, los tipos de unión POD (cláusula 9), las matrices de estos tipos y las versiones calificadas para CV de estos tipos (3.9.3) se denominan colectivamente tipos de POD "
9 (4): "Una estructura POD es una clase agregada que no tiene miembros de datos no estáticos de tipo no POD-estructura, no-POD-unión (o matriz de dichos tipos) o referencia, y no tiene usuario definir un operador de copia y ningún destructor definido por el usuario. De manera similar, una unión POD es una unión agregada que no tiene miembros de datos no estáticos de tipo no POD-struct, no-POD-union (o matriz de tales tipos) o referencia, y no tiene un operador de copia definido por el usuario ni un destructor definido por el usuario.
8.5.1 (1): "Un agregado es una matriz o clase (cláusula 9) sin constructores declarados por el usuario (12.1), sin miembros de datos no estáticos privados o protegidos (cláusula 11), sin clases base (cláusula 10) y sin funciones virtuales (10.3) ".
Me he topado con este término tipo POD unas cuantas veces. Qué significa eso?
Con C ++, Plain Old Data no solo significa que cosas como int, char, etc. son los únicos tipos utilizados. Plain Old Data realmente significa en la práctica que puede tomar una estructura desde una ubicación en la memoria a otra y las cosas funcionarán exactamente como lo esperaría (es decir, no explotar). Esto se rompe si su clase, o cualquier clase que contenga su clase, tiene como miembro que es un puntero o una referencia o una clase que tiene una función virtual. Esencialmente, si los punteros tienen que estar involucrados en algún lugar, no son datos antiguos.
El concepto de POD y el rasgo de tipo std::is_pod
quedarán obsoletos en C ++ 20. Vea this pregunta para más información.
Según tengo entendido, POD (PlainOldData) es solo un dato sin formato, no necesita:
- para ser construido,
- ser destruido,
- Tener operadores personalizados.
- No debe tener funciones virtuales,
- y no debe anular los operadores.
¿Cómo comprobar si algo es un POD? Bueno, hay una estructura para eso llamada 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)>
{ };
}
(Del encabezado type_traits)
Referencia:
Un objeto POD (datos antiguos) tiene uno de estos tipos de datos (un tipo fundamental, puntero, unión, estructura, matriz o clase) sin constructor. A la inversa, un objeto no POD es uno para el que existe un constructor. Un objeto POD comienza su vida útil cuando obtiene almacenamiento con el tamaño adecuado para su tipo y su vida útil finaliza cuando el almacenamiento para el objeto se reutiliza o se desasigna.
Los tipos PlainOldData tampoco deben tener ninguno de:
- Funciones virtuales (propias o heredadas)
- Clases base virtuales (directas o indirectas).
Una definición más suelta de PlainOldData incluye objetos con constructores; pero excluye a aquellos con cualquier cosa virtual. El problema importante con los tipos PlainOldData es que no son polimórficos. La herencia se puede hacer con los tipos de POD, sin embargo, solo debe hacerse para la herencia de implementación (reutilización de código) y no para el polimorfismo / subtipo.
Una definición común (aunque no estrictamente correcta) es que un tipo PlainOldData es cualquier cosa que no tenga una VeeTable.
En resumen, son todos los tipos de datos integrados (por ejemplo, int
, char
, float
, long
, unsigned char
, double
, etc.) y toda la agregación de datos POD. Sí, es una definición recursiva. ;)
Para ser más claros, un POD es lo que llamamos "una estructura": una unidad o un grupo de unidades que solo almacenan datos.
POD significa Datos antiguos lisos , es decir, una clase (ya sea definida con la struct
palabra clave o la class
palabra clave) sin funciones de constructores, destructores y miembros virtuales. El artículo de Wikipedia sobre POD se detalla un poco más y lo define como:
Una estructura de datos antigua simple en C ++ es una clase agregada que contiene solo PODS como miembros, no tiene un destructor definido por el usuario, ningún operador de asignación de copia definido por el usuario y ningún miembro no estático de tipo puntero a miembro.
Se pueden encontrar mayores detalles en esta respuesta para C ++ 98/03 . C ++ 11 cambió las reglas que rodean a la POD, relajándolas en gran medida, por lo que se necesita una respuesta de seguimiento aquí .
Ejemplos de todos los posibles casos no POD con static_assert
de C ++ 11 a C ++ 17
std::is_pod
se agregó en C ++ 11, así que consideremos ese estándar en adelante por ahora.
std::is_pod
se eliminará de C ++ 20 como se menciona en https://.com/a/48435532/895245 , actualicemos esto a medida que llegue el soporte para los reemplazos.
Las restricciones de POD se han relajado cada vez más a medida que el estándar evolucionó, mi objetivo es cubrir todas las relajaciones con el ejemplo.
libstdc ++ tiene un poquito de pruebas en: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc pero es demasiado poco Mantenedores: fusione esto si leyó este post. Soy un poco perezoso para ver todos los proyectos mencionados en: https://softwareengineering.stackexchange.com/questions/199708/is-there-a-compliance-test-for-c-compilers Si alguien hizo tal proyecto, él o ella se haría famosa
Por favor, ayúdame a matar a todos los TODO que faltan con tus ediciones, gurús de C ++.
#include <type_traits>
#include <vector>
int main() {
/* POD restrictions have become more and more relaxed as the standard evolved.
*
* std::is_pod was added in C++11, so let''s consider that standard onwards for now.
*/
#if __cplusplus >= 201103L
/* 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 i) {}
};
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& 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>());
}
/* 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 examples. Everything that does not fall in the non-POD examples. */
{
/* Can''t get more POD than this. */
{
struct C {};
static_assert(std::is_pod<C>());
}
/* Private member: became POD in C++11
* https://.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
}
/* Standard library containers are not, for the most part (all?),
* POD because they are not trivial, which can be seen directly from their
* interface definition in the standard.
* https://.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>>());
}
/* Array of POD is POD. */
{
struct C {};
static_assert(std::is_pod<C>());
static_assert(std::is_pod<C[]>());
}
}
#endif
}
Probado con:
for std in 11 14 17; do echo $std; g++-8 -std=c++$std pod.cpp; done
en Ubuntu 18.04.
Lista de todos los efectos POD conocidos por el hombre
Ya hay una lista en: https://.com/a/4178176/895245 pero quiero TODOS con ejemplos o enlaces a ejemplos, por lo menos, elimínelos:
-
__attribute__((packed))
se ignora en no POD: ignorando el atributo empaquetado debido al campo no POD desempaquetado que a su vez evita lamemcpy
segura de dichas clases.