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

979

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

paxos1977
источник
5
см. chat.stackoverflow.com/transcript/message/213026#213026 и сообщения следующего дня для обсуждения принятого ответа
Йоханнес Шауб - litb
2
Также stackoverflow.com/questions/4178175/…
Мигран Овсепян
@ paxos1977: Пожалуйста, измените свой выбор «решения» (в настоящее время ответ Хьюгилла), чтобы принципиально неправильный ответ не вводил в заблуждение гуглеров, которые здесь оказываются.
ура и hth. - Альф
Мы пришли к выводу, что строка в стиле c НЕ является типом POD, потому что 1.) указатель не является смежным со строковыми данными, и 2.) чтобы сделать строку типом POD, вам необходимо убедиться, что тип в нем был нулевой символ char в предопределенном размере типа POD, что приводило к неопределенному поведению.

Ответы:

695

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

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

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

Грег Хьюгилл
источник
35
Есть разница Внутренние типы - это встроенные языковые примитивы. Это типы POD, а также их совокупность (и другие POD).
Адам Райт
59
Типы POD имеют характеристики, которых нет у типов без POD. Например, если у вас есть глобальная, const, структура POD-типа, вы можете инициализировать ее содержимое с помощью скобок, оно помещается в постоянную память, и для ее инициализации не требуется генерировать код (конструктор или другой), потому что это часть образа программы. Это важно для встраиваемых пользователей, которые часто имеют жесткие ограничения на RAM, ROM или Flash.
Майк ДеСимоне
35
В C ++ 11 вы можете выполнить std :: is_pod <MyType> (), чтобы определить, является ли MyType POD.
allyourcode
7
В техническом отчете Бьярна Страуструпа о производительности C ++ говорится, что стандарт C ++ описывает POD как « тип данных, который совместим с эквивалентным типом данных в C в макете, инициализации и его способности копироваться с помощью memcpy ». Возможно, следует различать тип POD и структуру POD.
user34660
6
-1 Этот ответ по-прежнему в корне неверен и вводит в заблуждение по состоянию на 16 августа 2016 года: типы POD не ограничиваются типами классов.
ура и hth. - Альф
353

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

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

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

  • int, char, wchar_t, bool, float, doubleЯвляются стручки, так же как long/shortи signed/unsignedих версий.
  • указатели (включая указатель на функцию и указатель на член) являются POD,
  • enums это стручки
  • constили volatilePOD является POD.
  • a class, structили unionPOD - это 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). "

Стив Джессоп
источник
3
У вас есть формальный / менее формальный. Вы можете добавить правило. Встроенные типы и совокупности Встроенные типы (или что-то в этом роде). Кроме того, чтобы получить точное определение, нам нужно сделать знания простыми в использовании.
Мартин Йорк,
1
Вы немного ошибаетесь с битом «смещения при приведении к другому типу». Эти смещения применяются при приведении к базовому или производному классу. Таким образом, если вы преобразуете из указателя базового класса POD в не производный от POD класс, вы все равно можете столкнуться с корректировкой.
MSalters
1
@ Стив Джессоп: Почему мы вообще должны различать POD и не POD?
Лазер
6
@Lazer: это совсем другой вопрос, "как ведут себя PODы?" в отличие от «что означает POD?». Таким образом, разница связана с инициализацией (отсюда также использование memcpy для дублирования объектов), совместимостью с компоновкой структуры C для этого компилятора и повышением и понижением указателя. POD "действуют как типы C", не POD не гарантируют, что это так. Поэтому, если вы хотите, чтобы ваш тип работал как структура C, вы должны убедиться, что это POD, поэтому вам нужно знать разницу.
Стив Джессоп
4
@muntoo: действительно, я комментировал ответ, который цитирует устаревшую информацию из Википедии. Я мог бы отредактировать этот ответ, я полагаю, но я чувствую неприятности, если я хожу редактировать ответы других людей, чтобы согласиться с моим, независимо от того, насколько я прав.
Стив Джессоп
21

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

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

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

ugasoft
источник
13
Это правда, что мы иногда называем их «структура». Однако мы всегда ошибаемся, так как структура не обязательно является POD-типом.
Стив Джессоп
7
очевидно ... struct и class почти эквивалентны, но в «бизнесе» мы называем «struct» простым сборщиком данных, обычно без ctors и dtor, обычно с семантикой значений ...
ugasoft
2
Для меня было C ++ неправильно делать struct идентичной ключевому слову class или близкой к: struct только добавляет открытый доступ по умолчанию к классу. Мне было проще создавать C-подобные структуры, и у нас были бы PODы в день 0 c ++.
user1708042
ugasoft: ваш ответ может вводить в заблуждение - ваш комментарий объяснил недостающую деталь, что он используется на практике именно так, а не стандартно. Вау, 8 лет, ты вообще здесь? ;-)
hauron
За исключением строки, потому что вы не можете скопировать ее с помощью memcpy без предварительного определения длины строки.
12

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


Ссылка:

набиячлэвэли
источник
2
Неправильно, тип POD может иметь функции-члены или перегруженные операторы. (Но у него могут не быть виртуальные функции-члены.)
Колин Д. Беннет
@ColinDBennett Да, это правда. Извините за путаницу. Отредактировано в / из ответчика.
набиячлэвэли
10

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

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

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

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

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

amitabes
источник
Ваш ответ очень хороший, но этот вопрос получил ответ 8 лет назад, плюс еще несколько хороших ответов. Вы можете сделать больше для SO, если используете свои знания для ответа на вопросы, на которые еще нет ответа)))
mvidelgauz
10

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

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

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

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

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

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

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

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

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

plugwash
источник
8

Примеры всех случаев без 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
}

GitHub вверх по течению .

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

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.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
источник
4

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

ThomasMcLeod
источник
-7

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

Марк Кегель
источник
6
Указатели разрешены в структурах POD. Ссылки не являются.
j_random_hacker
1
Пассант здесь отсутствует.
icbytes