O que são tipos de POD em C ++?


978

Eu me deparei com esse termo tipo POD algumas vezes.
O que isso significa?



5
consulte chat.stackoverflow.com/transcript/message/213026#213026 e as mensagens do dia seguinte para discussão sobre a resposta aceita
Johannes Schaub - litb


@ paxos1977: Altere sua seleção de "solução" (atualmente a resposta da Hewgill) para que uma resposta fundamentalmente errada não engane os internautas que acabam aqui.
Saúde e hth. - Alf

Concluímos que uma string no estilo c NÃO é do tipo POD porque 1.) o ponteiro não é contíguo aos dados da string e 2.) para tornar uma string do tipo POD, você precisa garantir o tipo tinha um caracter nulo dentro do tamanho predefinido do tipo POD, levando a um comportamento indefinido.

Respostas:


695

POD significa Plain Old Data - ou seja, uma classe (definida com a palavra-chave structou a palavra-chave class) sem funções de construtores, destruidores e membros virtuais. O artigo da Wikipedia sobre POD entra em detalhes um pouco mais e define como:

Uma Estrutura de Dados Antiga Simples em C ++ é uma classe agregada que contém apenas PODS como membros, não possui destruidor definido pelo usuário, operador de atribuição de cópia definida pelo usuário e membros não estáticos do tipo ponteiro para membro.

Maiores detalhes podem ser encontrados nesta resposta para C ++ 98/03 . O C ++ 11 alterou as regras em torno do POD, relaxando-as bastante, necessitando de uma resposta de acompanhamento aqui .


35
Há uma diferença. Tipos intrínsecos são os primitivos da linguagem "incorporada". Os tipos de POD são esses, além de agregações desses (e outros PODs).
Adam Wright

59
Os tipos de POD têm características que os tipos não POD não. Por exemplo, se você possui uma estrutura global, const, do tipo POD, pode inicializar seu conteúdo com uma notação entre colchetes, ela é colocada na memória somente leitura e nenhum código precisa ser gerado para inicializá-lo (construtor ou outro), porque faz parte da imagem do programa. Isso é importante para pessoas incorporadas que geralmente têm restrições estreitas na RAM, ROM ou Flash.
Mike DeSimone

35
No C ++ 11, você pode executar std :: is_pod <MyType> () para saber se MyType é POD.
allyourcode

7
Relatório técnico de Bjarne Stroustrup sobre desempenho em C ++ afirma que o padrão C ++ descreve um POD como sendo " um tipo de dados compatível com o tipo de dados equivalente em C no layout, inicialização e sua capacidade de ser copiado com o memcpy ". Talvez deva ser feita uma distinção entre um tipo de POD e uma estrutura de POD.
user34660

6
−1 Esta resposta ainda é fundamentalmente errada e enganosa a partir de 16 de agosto de 2016: Os tipos de POD não estão restritos a tipos de classe.
Saúde e hth. - Alf

353

Muito informalmente:

Um POD é um tipo (incluindo classes) em que o compilador C ++ garante que não haverá "mágica" na estrutura: por exemplo, ponteiros ocultos para vtables, compensações que são aplicadas ao endereço quando ele é convertido para outros tipos ( pelo menos se o POD do alvo também), construtores ou destruidores. Grosso modo, um tipo é um POD quando as únicas coisas nele são tipos internos e combinações deles. O resultado é algo que "age como" um tipo C.

Menos informalmente:

  • int, char, wchar_t, bool, float, doubleEstão PODs, como são long/shorte signed/unsignedversões deles.
  • ponteiros (incluindo ponteiro para função e ponteiro para membro) são PODs,
  • enums são PODs
  • a constou volatilePOD é um POD.
  • uma class , structou unionde PODs é um POD, desde que todos os membros de dados não estáticos sejam publice não possua classe base nem construtores, destruidores ou métodos virtuais. Membros estáticos não impedem que algo seja um POD sob esta regra. Esta regra foi alterada no C ++ 11 e certos membros particulares são permitidos: Uma classe com todos os membros privados pode ser uma classe POD?
  • A Wikipedia está errada ao dizer que um POD não pode ter membros do tipo ponteiro para membro. Ou melhor, está correto para a redação do C ++ 98, mas o TC1 tornou explícito que ponteiros para membro são POD.

Formalmente (padrão C ++ 03):

3.9 (10): "Tipos aritméticos (3.9.1), tipos de enumeração, tipos de ponteiros e ponteiros para tipos de membros (3.9.2) e versões qualificadas para cv desses tipos (3.9.3) são coletivamente tipos escalares de chamadores. tipos, tipos de estrutura POD, tipos de união POD (cláusula 9), matrizes desses tipos e versões qualificadas para cv desses tipos (3.9.3) são coletivamente chamados de tipos POD "

9 (4): "Uma estrutura POD é uma classe agregada que não possui membros de dados não estáticos do tipo estrutura não POD, união não POD (ou matriz de tais tipos) ou referência e não possui nenhuma definir operador de cópia e nenhum destruidor definido pelo usuário Da mesma forma, uma união POD é uma união agregada que não possui membros de dados não estáticos do tipo não-POD-struct, não-POD-união (ou matriz de tais tipos) ou referência, e não possui operador de cópia definido pelo usuário nem destruidor definido pelo usuário.

8.5.1 (1): "Um agregado é uma matriz ou classe (cláusula 9) sem construtores declarados pelo usuário (12.1), sem membros de dados não estáticos privados ou protegidos (cláusula 11), sem classes base (cláusula 10) e nenhuma função virtual (10.3). "


3
Você tem formal / menos formal. Você pode adicionar uma regra de ouro. Tipos incorporados e agregações de tipos Incorporados (ou algo parecido). Além de obter a definição exata, precisamos facilitar o uso do conhecimento.
Martin York

1
Você está um pouco errado no bit "offsets when cast_to another type". Essas compensações são aplicadas ao transmitir para uma classe base ou derivada. Portanto, se você converter de um ponteiro de classe base do POD para uma classe não derivada do POD, ainda poderá encontrar um ajuste.
MSalters 29/09/08

1
@ Steve Jessop: Por que precisamos diferenciar entre POD e não POD?
Lazer

6
@Lazer: essa é outra questão: "como os PODs se comportam?" em oposição a "o que significa POD?". Em resumo, a diferença está relacionada à inicialização (portanto, também ao uso do memcpy para duplicar objetos), à compatibilidade com o layout C struct para esse compilador e ao apontador para cima e para baixo. Os PODs "agem como tipos C", não os PODs não são garantidos. Portanto, se você deseja que seu tipo atue de maneira portável como uma estrutura C, você deve garantir que seja POD, para que você saiba a diferença.
18710 Steve Jobs

4
@muntoo: foi, realmente, eu estava comentando a resposta que cita informações desatualizadas da Wikipedia. Suponho que poderia editar essa resposta, mas sinto problemas se sair editando a resposta de outras pessoas para concordar com a minha, não importa o quão certo eu esteja.
precisa

21

Dados antigos simples

Em suma, é tudo embutido tipos de dados (por exemplo int, char, float, long, unsigned char, double, etc.) e todos os dados de agregação de POD. Sim, é uma definição recursiva. ;)

Para ser mais claro, um POD é o que chamamos de "uma estrutura": uma unidade ou um grupo de unidades que apenas armazena dados.


13
É verdade que às vezes as chamamos de 'uma estrutura'. No entanto, sempre estamos errados ao fazer isso, pois uma estrutura não é necessariamente do tipo POD.
Steve Jessop

7
obviamente ... struct e classe são quase equivalentes, mas no "negócio" chamamos 'a struct' de um coletor de dados simples, geralmente sem ctors e dtor, geralmente com semântica de valores ...
ugasoft

2
Para mim, era errado o C ++ tornar o struct idêntico à palavra-chave da classe ou próximo a: struct apenas adiciona acesso público padrão à classe. Eu era mais simples de criar estruturas do tipo C e teríamos PODs no dia 0 de c ++.
user1708042

ugasoft: sua resposta pode ser enganosa - seu comentário explicou os detalhes ausentes que são usados ​​assim na prática, e não o padrão. Whoa, 8 anos, você está aqui? ;-)
hauron 10/03/16

Com exceção de uma sequência, porque você não pode copiá-la com memcpy sem antes determinar o comprimento da sequência.

12

Pelo que entendi POD (PlainOldData) é apenas um dado bruto - ele não precisa:

  • para ser construído,
  • ser destruído,
  • ter operadores personalizados.
  • Não deve ter funções virtuais,
  • e não deve substituir os operadores.

Como verificar se algo é um POD? Bem, existe uma estrutura para isso chamada 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)>
  { };
}

(Do cabeçalho type_traits)


Referência:


2
Incorreto, um tipo de POD pode ter funções membro ou operadores sobrecarregados. (Mas não pode ter funções membro virtuais.)
Colin D Bennett

@ColinDBennett Sim, é verdade. Desculpe pela confusão. Editado para dentro / fora da resposta.
20515

10

Um objeto POD (dados antigos simples) possui um desses tipos de dados - um tipo fundamental, ponteiro, união, estrutura, matriz ou classe - sem construtor. Por outro lado, um objeto não-POD é aquele para o qual existe um construtor. Um objeto POD inicia sua vida útil quando obtém armazenamento com o tamanho adequado para seu tipo e sua vida útil termina quando o armazenamento do objeto é reutilizado ou desalocado.

Os tipos PlainOldData também não devem ter:

  • Funções virtuais (próprias ou herdadas)
  • Classes base virtuais (diretas ou indiretas).

Uma definição mais flexível de PlainOldData inclui objetos com construtores; mas exclui aqueles com qualquer coisa virtual. O problema importante com os tipos PlainOldData é que eles não são polimórficos. A herança pode ser feita com tipos de POD, no entanto, deve ser feita apenas para ImplementationInheritance (reutilização de código) e não para polimorfismo / subtipagem.

Uma definição comum (embora não estritamente correta) é que um tipo PlainOldData é qualquer coisa que não tenha uma VeeTable.


A resposta Yuor é muito boa, mas esta pergunta aceitou resposta há 8 anos, além de várias outras boas respostas. Você pode contribuir mais para isso, se você usá-lo conhecimento para responder a perguntas que ainda não foram respondidas)))
mvidelgauz

10

Por que precisamos diferenciar entre PODs e não-PODs?

O C ++ começou sua vida como uma extensão de C. Enquanto o C ++ moderno não é mais um superconjunto estrito de C, as pessoas ainda esperam um alto nível de compatibilidade entre os dois.

Grosso modo, um tipo de POD é compatível com C e talvez igualmente importante é compatível com certas otimizações da ABI.

Para ser compatível com C, precisamos satisfazer duas restrições.

  1. O layout deve ser o mesmo que o tipo C correspondente.
  2. O tipo deve ser passado e retornado das funções da mesma maneira que o tipo C correspondente.

Certos recursos do C ++ são incompatíveis com isso.

Os métodos virtuais exigem que o compilador insira um ou mais ponteiros nas tabelas de métodos virtuais, algo que não existe em C.

Os construtores de cópias definidos pelo usuário, os construtores de movimentação, as atribuições e os destruidores de cópia têm implicações na passagem e no retorno de parâmetros. Muitos C ABIs transmitem e retornam pequenos parâmetros nos registradores, mas as referências passadas ao construtor / atribuição / destruidor definido pelo usuário podem funcionar apenas com locais de memória.

Portanto, é necessário definir quais tipos podem ser "compatíveis com C" e quais não. O C ++ 03 foi um pouco rigoroso nesse aspecto, qualquer construtor definido pelo usuário desabilitaria os construtores internos e qualquer tentativa de adicioná-los novamente resultaria em sua definição pelo usuário e, portanto, o tipo sem pod. O C ++ 11 abriu bastante as coisas, permitindo ao usuário reintroduzir os construtores internos.


8

Exemplos de todos os casos não POD com static_assertefeitos de C ++ 11 a C ++ 17 e POD

std::is_pod foi adicionado no C ++ 11, então vamos considerar esse padrão por enquanto.

std::is_podserá removido do C ++ 20, conforme mencionado em https://stackoverflow.com/a/48435532/895245 , vamos atualizar isso à medida que o suporte chegar para as substituições.

As restrições de POD tornaram-se cada vez mais relaxadas à medida que o padrão evoluiu, pretendo cobrir todos os relaxamentos no exemplo através do ifdefs.

O libstdc ++ tem poucos testes em: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc, mas muito pouco. Mantenedores: mescle isso se você ler esta publicação. Estou com preguiça de verificar todos os projetos de testes do C ++ mencionados em: /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 upstream .

Testado com:

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

no Ubuntu 18.04, GCC 8.2.0.


4

O conceito de POD e a característica do tipo std::is_podserão preteridos no C ++ 20. Veja esta pergunta para mais informações.


-7

Com o C ++, Plain Old Data não significa apenas que coisas como int, char, etc são os únicos tipos usados. Dados antigos simples realmente significam, na prática, que você pode levá-lo de um local para outro, na memória, e tudo funcionará exatamente como você esperaria (ou seja, não explodir). Isso é interrompido se sua classe, ou qualquer classe que ela contiver, tiver como membro um ponteiro ou uma referência ou uma classe que tenha uma função virtual. Essencialmente, se os ponteiros precisam estar envolvidos em algum lugar, não são dados antigos simples.


6
Ponteiros são permitidos em estruturas de POD. Referências não são.
j_random_hacker

1
Passant está faltando aqui.
icbytes 16/09
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.