¿Cuáles son los tipos de POD en C ++?

978

Me he encontrado con este término tipo POD varias veces.
Qué significa eso?

paxos1977
fuente
55
consulte chat.stackoverflow.com/transcript/message/213026#213026 y los mensajes del día siguiente para discutir la respuesta aceptada
Johannes Schaub - litb
2
También stackoverflow.com/questions/4178175/…
Mihran Hovsepyan
@ paxos1977: Cambie su selección de "solución" (actualmente la respuesta de Hewgill) para que una respuesta fundamentalmente incorrecta no engañe a los googlers que terminan aquí.
Saludos y hth. - Alf
Hemos concluido que una cadena de estilo c NO es un tipo de POD porque 1.) el puntero no es contiguo a los datos de la cadena, y 2.) para que una cadena sea un tipo de POD, deberá asegurarse de que el tipo tenía un carácter de término nulo dentro del tamaño predefinido del tipo POD, lo que conduce a un comportamiento indefinido.

Respuestas:

695

POD significa Plain Old Data , es decir, una clase (ya sea definida con la palabra clave structo la palabra clave class) sin constructores, destructores y funciones de miembros virtuales. El artículo de Wikipedia sobre POD entra en un poco más de detalle y lo define como:

Una estructura de datos antigua y simple en C ++ es una clase agregada que contiene solo PODS como miembros, no tiene destructor definido por el usuario, ni operador de asignación de copia definido por el usuario, ni miembros no estáticos de tipo puntero a miembro.

Se pueden encontrar más detalles en esta respuesta para C ++ 98/03 . C ++ 11 cambió las reglas que rodean a POD, relajándolas en gran medida, por lo que se necesita una respuesta de seguimiento aquí .

Greg Hewgill
fuente
35
Hay una diferencia Los tipos intrínsecos son las primitivas del lenguaje "incorporado". Los tipos de POD son estos, además de las agregaciones de estos (y otros POD).
Adam Wright
59
Los tipos POD tienen características que los tipos no POD no tienen. Por ejemplo, si tiene una estructura de tipo POD global, constante, puede inicializar su contenido con notación de llaves, se coloca en la memoria de solo lectura y no es necesario generar código para inicializarla (constructor u otro), porque es parte de la imagen del programa. Esto es importante para las personas integradas que a menudo tienen restricciones estrictas en RAM, ROM o Flash.
Mike DeSimone
35
En C ++ 11, puede hacer std :: is_pod <MyType> () para saber si MyType es POD.
allyourcode
77
El informe técnico de Bjarne Stroustrup sobre el rendimiento de C ++ establece que el estándar C ++ describe un POD como " un tipo de datos que es compatible con el tipo de datos equivalente en C en el diseño, la inicialización y su capacidad de copiarse con memcpy ". Quizás debería hacerse una distinción entre un tipo de POD y una estructura de POD.
user34660
66
−1 Esta respuesta sigue siendo fundamentalmente errónea y engañosa a partir del 16 de agosto de 2016: los tipos de POD no están restringidos a los tipos de clase.
Saludos y hth. - Alf
353

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 a otros tipos ( al menos si el POD del objetivo también), constructores o destructores. Hablando en términos generales, un tipo es un POD cuando las únicas cosas en él son tipos incorporados y combinaciones de ellos. El resultado es algo que "actúa como" un tipo C.

Menos informalmente:

  • int, char, wchar_t, bool, float, doubleSon vainas, como son long/shorty signed/unsignedversiones de ellos.
  • los punteros (incluidos puntero a función y puntero a miembro) son POD,
  • enums son POD
  • a consto volatilePOD es un POD.
  • a class, structo unionde PODs es un POD siempre que todos los miembros de datos no estáticos lo sean public, y no tenga una clase base ni constructores, destructores o métodos virtuales. Los miembros estáticos no impiden que algo sea un POD bajo esta regla. Esta regla ha cambiado en C ++ 11 y ciertos miembros privados están permitidos: ¿Puede una clase con todos los miembros privados 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 miembro 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 los tipos de puntero a miembro (3.9.2) y las versiones calificadas por cv de estos tipos (3.9.3) son tipos escalares de llamada colectiva. Escalar tipos, tipos de estructura POD, tipos de unión POD (cláusula 9), matrices de tales tipos y versiones calificadas por cv de estos tipos (3.9.3) se denominan colectivamente tipos POD "

9 (4): "Una estructura POD es una clase agregada que no tiene miembros de datos no estáticos de tipo estructura no POD, unión no POD (o matriz de tales tipos) o referencia, y no tiene defina el operador de copia y no el 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 operador de copia definido por el usuario ni 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) ".

Steve Jessop
fuente
3
Tienes formal / menos formal. Puede agregar una regla general. Tipos incorporados y agregaciones de tipos incorporados (o algo así). Además de obtener la definición exacta, necesitamos que el conocimiento sea fácil de usar.
Martin York
1
Estás un poco equivocado en el bit "compensaciones cuando se lanza a otro tipo". Esas compensaciones se aplican al lanzar a una clase base o derivada. Entonces, si lanza desde un puntero de clase base POD a una clase no derivada de POD, aún puede encontrar un ajuste.
MSalters
1
@ Steve Jessop: ¿Por qué necesitamos diferenciar entre POD y no POD?
Lazer
66
@Lazer: esa es otra pregunta, "¿cómo se comportan los POD?" en oposición a "¿qué significa POD?". En resumen, la diferencia se relaciona con la inicialización (por lo tanto, también el uso de memcpy para duplicar objetos), la compatibilidad con el diseño de estructura de C para ese compilador y el puntero hacia arriba y hacia abajo. Los POD "actúan como tipos C", no se garantiza que los no POD lo hagan. Entonces, si desea que su tipo actúe de manera portátil como una estructura C, debe asegurarse de que sea POD, por lo que debe saber la diferencia.
Steve Jessop
44
@muntoo: ha sido, realmente estaba comentando la respuesta que cita información desactualizada de Wikipedia. Supongo que podría editar esa respuesta, pero huelo problemas si edito la respuesta de otras personas para estar de acuerdo con la mía, no importa lo acertada que sea.
Steve Jessop
21

Datos antiguos simples

En resumen, todo es incorporado en los tipos de datos (por ejemplo int, char, float, long, unsigned char, double, etc.) y todos 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.

ugasoft
fuente
13
Es cierto que a veces los llamamos 'una estructura'. Sin embargo, siempre nos equivocamos al hacerlo, ya que una estructura no es necesariamente un tipo POD.
Steve Jessop
77
obviamente ... estructura y la clase son casi equivalentes, pero en "el negocio" que llaman 'una estructura' un colector de datos simple, por lo general sin ctors y dtor, por lo general con la semántica de valor ...
ugasoft
2
Para mí fue un error de C ++ hacer que struct sea idéntico a la palabra clave de clase o cercano a: struct solo agrega acceso público predeterminado a la clase. Era más simple hacer estructuras tipo C y habríamos tenido POD en el día 0 de c ++.
user1708042
ugasoft: su respuesta puede ser engañosa: su comentario explica los detalles faltantes de que se usa así en la práctica, en lugar de ser estándar. Whoa, 8 años, ¿estás aquí? ;-)
hauron
Con la excepción de una cadena porque no puede copiarla con memcpy sin determinar primero la longitud de la cadena.
12

Según tengo entendido, POD (PlainOldData) es solo un dato sin procesar, no necesita:

  • para ser construido,
  • ser destruido
  • tener operadores personalizados.
  • No debe tener funciones virtuales,
  • y no debe anular a los operadores.

¿Cómo verificar 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:

набиячлэвэли
fuente
2
Incorrecto, un tipo de POD puede tener funciones miembro u operadores sobrecargados. (Pero puede que no tenga funciones de miembro virtual.)
Colin D Bennett
@ColinDBennett Sí, eso es cierto. Perdón por la confusion. Editado dentro / fuera de la respuesta.
набиячлэвэли
10

Un objeto POD (datos antiguos simples) tiene uno de estos tipos de datos: un tipo fundamental, puntero, unión, estructura, matriz o clase, sin constructor. Por el contrario, un objeto que no es POD es aquel 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 del objeto se reutiliza o se desasigna.

Los tipos PlainOldData tampoco deben tener ninguno de:

  • Funciones virtuales (ya sean propias o heredadas)
  • Clases base virtuales (directas o indirectas).

Una definición más flexible de PlainOldData incluye objetos con constructores; pero excluye a aquellos con algo virtual. El problema importante con los tipos PlainOldData es que no son polimórficos. La herencia se puede hacer con tipos de POD, sin embargo, solo se debe hacer para ImplementationInheritance (reutilización de código) y no para polimorfismo / subtipo.

Una definición común (aunque no estrictamente correcta) es que un tipo PlainOldData es cualquier cosa que no tenga una VeeTable.

amitabes
fuente
La respuesta de Yuor es muy buena, pero esta pregunta ha aceptado la respuesta hace 8 años, además de varias otras buenas respuestas. Puede contribuir más a SO si usa su conocimiento para responder preguntas que aún no se han respondido)))
mvidelgauz
10

¿Por qué necesitamos diferenciar entre POD y no POD?

C ++ comenzó su vida como una extensión de C. Mientras que C ++ moderno ya no es un superconjunto estricto de C, la gente todavía espera un alto nivel de compatibilidad entre los dos.

Hablando en términos generales, un tipo de POD es un tipo que es compatible con C y quizás igualmente importante es compatible con ciertas optimizaciones ABI.

Para ser compatible con C, necesitamos satisfacer dos restricciones.

  1. El diseño debe ser el mismo que el tipo C correspondiente.
  2. El tipo debe pasarse y devolverse desde las funciones de la misma manera que el tipo C correspondiente.

Ciertas características de C ++ son incompatibles con esto.

Los métodos virtuales requieren que el compilador inserte uno o más punteros en las tablas de métodos virtuales, algo que no existe en C.

Los constructores de copia definidos por el usuario, los constructores de movimiento, las asignaciones de copia y los destructores tienen implicaciones para el paso y la devolución de parámetros. Muchas C ABI pasan y devuelven pequeños parámetros en los registros, pero las referencias pasadas al constructor / asignación / destructor definido por el usuario solo pueden funcionar con ubicaciones de memoria.

Por lo tanto, es necesario definir qué tipos pueden esperarse que sean "compatibles con C" y qué tipos no. C ++ 03 era algo demasiado estricto a este respecto, cualquier constructor definido por el usuario deshabilitaría los constructores incorporados y cualquier intento de volver a agregarlos resultaría en que estuvieran definidos por el usuario y, por lo tanto, el tipo no fuera de pod. C ++ 11 abrió bastante las cosas, permitiendo al usuario reintroducir los constructores integrados.

lavado
fuente
8

Ejemplos de todos los casos sin POD con static_assertefectos de C ++ 11 a C ++ 17 y POD

std::is_pod se agregó en C ++ 11, así que consideremos ese estándar en adelante por ahora.

std::is_podserá eliminado de C ++ 20 como se menciona en https://stackoverflow.com/a/48435532/895245 , actualice esto a medida que llegue el soporte para los reemplazos.

Las restricciones de POD se han vuelto cada vez más relajadas a medida que el estándar evolucionó, mi objetivo es cubrir todas las relajaciones en el ejemplo a través de ifdefs.

libstdc ++ tiene una pequeña prueba 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 muy poco Mantenedores: fusionen esto si leen esta publicación. Soy flojo para ver todos los proyectos de prueba de C ++ mencionados en: /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
}

GitHub aguas arriba .

Probado con:

for std in 11 14 17; do echo $std; g++-8 -Wall -Werror -Wextra -pedantic -std=c++$std pod.cpp; done

en Ubuntu 18.04, GCC 8.2.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
4

El concepto de POD y el rasgo de tipo std::is_podquedarán en desuso en C ++ 20. Vea esta pregunta para más información.

ThomasMcLeod
fuente
-7

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 llevar una estructura de memoria de una ubicación en la memoria a otra y las cosas funcionarán exactamente como se esperaría (es decir, no explotar). Esto se rompe si su clase, o cualquier clase que contenga su clase, tiene como miembro un puntero o una referencia o una clase que tiene una función virtual. Esencialmente, si los punteros tienen que estar involucrados en alguna parte, no son datos antiguos simples.

Mark Kegel
fuente
66
Los punteros están permitidos en estructuras POD. Las referencias no lo son.
j_random_hacker
1
Passant falta aquí.
icbytes