Что такое типы POD в C ++? - PullRequest
       119

Что такое типы POD в C ++?

868 голосов
/ 28 сентября 2008

Я встречал этот термин POD-типа несколько раз. Что это значит?

Ответы [ 9 ]

620 голосов
/ 28 сентября 2008

POD означает Обычные старые данные - то есть класс (определен ли с ключевым словом struct или ключевым словом class) без конструкторов, деструкторов и виртуальных членов функции. Статья Википедии о POD углубляется в детали и определяет ее как:

Простая старая структура данных в C ++ - это агрегатный класс, который содержит только PODS в качестве членов, не имеет пользовательского деструктора, пользовательского оператора назначения копирования и нестатических членов типа указатель на член.

Более подробную информацию можно найти в этом ответе на C ++ 98/03 . C ++ 11 изменил правила, связанные с POD, значительно ослабив их, поэтому требует ответа здесь .

320 голосов
/ 28 сентября 2008

Очень неформально:

POD - это тип (включая классы), в котором компилятор C ++ гарантирует, что в структуре не будет "волшебства": например, скрытые указатели на vtables, смещения, которые применяются к адресу при его приведении к другие типы (по крайней мере, если POD цели тоже), конструкторы или деструкторы. Грубо говоря, тип - это POD, когда в нем есть только встроенные типы и их комбинации. Результатом является то, что «действует как» тип C.

Менее неформально:

  • int, char, wchar_t, bool, float, double являются POD, как и версии long/short и signed/unsigned.
  • указатели (включая указатель на функцию и указатель на член) являются POD,
  • enums - это POD
  • a const или volatile POD - это POD.
  • a class, struct или union POD - это POD при условии, что все нестатические члены-данные имеют значение public, и у него нет базового класса и нет конструкторов, деструкторов или виртуальных методов. Статические члены не перестают быть POD согласно этому правилу. Это правило изменилось в C ++ 11, и разрешены некоторые частные члены: Может ли класс со всеми закрытыми членами быть классом POD?
  • Википедия не может сказать, что POD не может иметь членов типа указатель на член. Или, вернее, это правильно для формулировки C ++ 98, но TC1 ясно дал понять, что указатели на член - это POD.

Формально (стандарт C ++ 03):

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). "

17 голосов
/ 28 сентября 2008

Обычные старые данные

Короче говоря, это все встроенные типы данных (например, int, char, float, long, unsigned char, double и т. Д.) И все агрегации данных POD. Да, это рекурсивное определение. ;)

Для большей ясности, POD - это то, что мы называем "структурой": единица или группа единиц, которые просто хранят данные.

10 голосов
/ 08 декабря 2013

Как я понимаю 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)


Ссылка:

8 голосов
/ 16 августа 2016

Объект POD (обычные старые данные) имеет один из этих типов данных - фундаментальный тип, указатель, объединение, структура, массив или класс - без конструктора. И наоборот, не POD-объект - это объект, для которого существует конструктор. Срок службы объекта POD начинается, когда он получает хранилище с правильным размером для своего типа, и срок его службы заканчивается, когда хранилище для объекта используется повторно или освобождается.

Типы PlainOldData также не должны содержать:

  • Виртуальные функции (как свои, так и унаследованные)
  • Виртуальные базовые классы (прямые или косвенные).

Более слабое определение PlainOldData включает объекты с конструкторами; но исключает тех, у кого есть что-нибудь виртуальное. Важная проблема с типами PlainOldData заключается в том, что они не являются полиморфными. Наследование может быть сделано с типами POD, однако это должно быть сделано только для реализацииInheritance (повторное использование кода), а не полиморфизм / подтип.

Распространенным (хотя и не совсем правильным) определением является то, что тип PlainOldData - это все, что не имеет VeeTable.

6 голосов

Примеры всех случаев, не связанных с 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, упомянутые по адресу: https://softwareengineering.stackexchange.com/questions/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
        // https://stackoverflow.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
        }

        // Most standard library containers are not POD because they are not trivial,
        // which can be seen directly from their interface definition in the standard.
        // https://stackoverflow.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>>(), "");
            // Some might be though:
            // https://stackoverflow.com/questions/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:
    // https://stackoverflow.com/questions/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
        // https://stackoverflow.com/questions/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 .

Протестировано с:

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.

3 голосов
/ 29 ноября 2018

Почему мы должны различать POD и не POD вообще?

C ++ начал свою жизнь как расширение C. Хотя современный C ++ больше не является строгим надмножеством C, люди все еще ожидают высокого уровня совместимости между ними.

Грубо говоря, тип POD - это тип, который совместим с C и, что не менее важно, совместим с некоторыми оптимизациями ABI.

Чтобы быть совместимым с C, нам нужно выполнить два ограничения.

  1. Расположение должно совпадать с соответствующим типом C.
  2. Тип должен передаваться и возвращаться из функций так же, как и соответствующий тип C.

Некоторые функции C ++ несовместимы с этим.

Виртуальные методы требуют, чтобы компилятор вставлял один или несколько указателей в таблицы виртуальных методов, чего нет в C.

Пользовательские конструкторы копирования, конструкторы перемещения, назначения копирования и деструкторы имеют значение для передачи и возврата параметров. Многие C ABI передают и возвращают небольшие параметры в регистрах, но ссылки, передаваемые пользовательскому конструктору / ассигменту / деструктору, могут работать только с ячейками памяти.

Таким образом, необходимо определить, какие типы могут быть «совместимыми с C», а какие нет. C ++ 03 был несколько чрезмерно строг в этом отношении. C ++ 11 довольно многое открыл.

3 голосов
/ 25 января 2018

Концепция POD и черта типа std::is_pod устарели в C ++ 20. См. этот вопрос для получения дополнительной информации.

0 голосов
/ 29 сентября 2008

В C ++ обычные старые данные не просто означают, что используются только такие типы, как int, char и т. Д. Обычные старые данные на практике означают, что вы можете перенести struct memcpy из одного места в памяти в другое, и все будет работать точно так, как вы ожидаете (то есть не взорваться). Это нарушается, если ваш класс или любой класс, содержащийся в вашем классе, имеет в качестве члена указатель, ссылку или класс, имеющий виртуальную функцию. По сути, если указатели должны быть где-то задействованы, это не Plain Old Data.

...