Які типи POD у С ++?


977

Я кілька разів стикався з цим терміном типу POD.
Що це означає?


1
Також дивіться http://stackoverflow.com/questions/2293796
Lazer

5
див. chat.stackoverflow.com/transcript/message/213026#213026 та повідомлення наступного дня для обговорення прийнятої відповіді
Йоганнес Шауб -


@ paxos1977: Будь ласка, змініть свій вибір "рішення" (зараз відповідь Hewgill), щоб принципово неправильна відповідь не вводила в оману гуглерів, які опиняються тут.
ура та хт. - Альф

Ми зробили висновок, що рядок у стилі c НЕ є типом POD, тому що 1.) покажчик не є суміжним із рядковими даними та 2.) для того, щоб зробити рядок типу POD, вам потрібно буде забезпечити тип мав нульову позначку в ньому в межах заздалегідь визначеного розміру типу POD, що призводить до невизначеної поведінки.

Відповіді:


694

POD позначає Plain Old Data - тобто клас (визначений ключовим словом structчи ключовим словом class) без конструкторів, деструкторів та функцій віртуальних членів. Стаття Вікіпедії про ПОД детальніше і детальніше визначає її як:

Звичайна стара структура даних в C ++ - це сукупний клас, який містить лише PODS як членів, не має визначеного користувачем деструктора, не визначеного користувачем оператора призначення копії та нестатичних членів типу вказівника на член.

Більш детальну інформацію можна знайти у цій відповіді на C ++ 98/03 . C ++ 11 змінив правила навколо ПДД, сильно розслабивши їх, тим самим вимагаючи подальшої відповіді тут .


34
Є різниця. Внутрішні типи - це "вбудовані" мовні примітиви. Ці типи ПДД, а також сукупність цих (та інших ПОД).
Адам Райт

59
Типи POD мають характеристики, які не мають типів POD. Наприклад, якщо у вас є глобальна, const, структура типу POD, ви можете ініціалізувати її вміст за допомогою брекет-позначень, він вставляється в пам'ять, доступну лише для читання, і не потрібно створювати код для його ініціалізації (конструктор чи іншим способом), тому що це частина зображення програми. Це важливо для вбудованих людей, які часто мають жорсткі обмеження в оперативній пам'яті, ROM або Flash.
Майк Десимоне

34
У C ++ 11 ви можете зробити std :: is_pod <MyType> (), щоб визначити, чи MyType є POD.
allyourcode

7
Технічний звіт Bjarne Stroustrup щодо продуктивності C ++ зазначає, що стандарт C ++ описує POD як " тип даних, сумісний з еквівалентним типом даних на C у макеті, ініціалізації та його здатності копіювати memcpy ". Можливо, слід розрізняти тип POD та структуру POD.
користувач34660

6
−1 Ця відповідь досі є принципово неправильною та оманливою станом на 16 серпня 2016 року: типи POD не обмежуються типом класів.
ура та хт. - Альф

352

Дуже неофіційно:

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

Менше неофіційно:

  • int, char, wchar_t, bool, float, doubleЄ стручки, так само як long/shortі signed/unsignedїх версій.
  • покажчики (включаючи покажчик на функцію та вказівник на члена) - це ПОД,
  • enums є ПОД
  • a constабо volatilePOD - це POD.
  • a class, structабо unionPODs - це POD, за умови, що всі нестатичні члени даних є public, і він не має базового класу і не має конструкторів, деструкторів або віртуальних методів. Статичні члени не припиняють щось бути ПОД за цим правилом. Це правило змінилося в C ++ 11, і деякі приватні члени дозволені: Чи може клас із усіма приватними членами бути класом POD?
  • У Вікіпедії неправильно сказано, що ПОД не може мати членів типу вказівника на члена. А точніше, це правильне для формулювання C ++ 98, але TC1 чітко вказав, що покажчики на члена є POD.

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

3.9 (10): "Арифметичні типи (3.9.1), типи перерахування, типи вказівників та вказівники на типи членів (3.9.2) та версії цих типів (3.9.3), що мають кваліфікацію cv, - це колективно скалярні типи скалярних типів. типи, типи POD-структура, типи підключення POD (п. 9), масиви таких типів і CV-кваліфіковані версії цих типів (3.9.3) разом називаються типами POD

9 (4): "POD-структура - це сукупний клас, який не має нестатичних членів даних типу non-POD-структура, не-POD-об'єднання (або масив таких типів) або посилання, і не має користувача- визначте оператор копіювання і не визначений користувачем деструктор. Подібним чином, підключення POD - це сукупний об'єднання, що не має нестатичних членів даних типу не-POD-структура, не-POD-об'єднання (або масив таких типів) або посилання, і не має визначеного користувачем оператора копіювання і не визначеного користувачем деструктора.

8.5.1 (1): "Сукупність - це масив або клас (п. 9) без оголошених користувачем конструкторів (12.1), без приватних або захищених нестатичних членів даних (п. 11), без базових класів (п. 10) і немає віртуальних функцій (10.3). "


3
У вас формально / менш формально. Ви можете додати правило. Побудований у типах та агрегатах. Вбудований у типах (чи щось подібне). Окрім того, щоб отримати точне визначення, нам потрібно зробити знання простими у використанні.
Мартін Йорк,

1
Ви трохи помиляєтесь у біті "компенсації, коли cast_to іншого типу". Ці компенсації застосовуються під час трансляції до базового чи похідного класу. Отже, якщо ви перейдете з вказівника базового класу POD на клас, який не є POD, ви все ще можете зіткнутися з коригуванням.
MSalters

1
@Steve Jessop: Чому нам взагалі потрібно розмежовувати POD та не POD?
Лазер

6
@Lazer: це зовсім інше питання, "як поводяться PODS?" на відміну від "що означає ПОД?". Підсумовуючи різницю, різниця стосується ініціалізації (отже, також використання memcpy для дублювання об'єктів), сумісності з компонуванням структури C для цього компілятора та покажчика вгору та вниз. ПОД "діють як типи С", а не-ПОД не гарантують цього. Тож якщо ви хочете, щоб ваш тип портативно діяв як C-структура, ви повинні переконатися, що він є POD, тому вам потрібно знати різницю.
Стів Джессоп

4
@muntoo: так, справді я коментував відповідь, яка цитує застарілу інформацію з Вікіпедії. Я міг би відредагувати цю відповідь, але, мабуть, відчуваю неприємності, якщо обійдусь редагуванням відповіді інших людей, щоб погодитися з моєю, незалежно від того, наскільки я правда думаю.
Стів Джессоп

20

Прості старі дані

Коротше кажучи, це все вбудовані типи даних (наприклад int, char, float, long, unsigned char, doubleі т.д.) і все агрегування даних POD. Так, це рекурсивне визначення. ;)

Щоб було зрозуміліше, ПОД - це те, що ми називаємо "структура": одиниця або група одиниць, які просто зберігають дані.


12
Це правда, що ми їх іноді називаємо "структура". Однак ми завжди робимо це неправильно, оскільки структура не є обов'язково типом POD.
Стів Джессоп

6
очевидно ... структура і клас майже рівнозначні, але в "бізнесі" ми називаємо "структуру" простим збирачем даних, як правило, без ctors і dtor, як правило, зі значенням семантики ...
ugasoft

2
Для мене C ++ було неправильним робити структуру ідентичною ключовому слову класу або близькою до: структура лише додає загальнодоступний доступ до класу за замовчуванням. Мені було простіше робити С-подібні структури, і у нас були б ПОД в 0 день с ++.
user1708042

ugasoft: ваша відповідь може ввести в оману - ваш коментар пояснив відсутність деталей про те, що вона використовується на практиці, а не стандартна. О, 8 років, ти навіть тут? ;-)
hauron

За винятком рядка, оскільки ви не можете скопіювати його memcpy без попереднього визначення довжини рядка.

12

Як я розумію, POD (PlainOldData) - це лише необроблені дані - йому не потрібно:

  • будувати,
  • знищити,
  • мати власні оператори.
  • Не повинно бути віртуальних функцій,
  • і не повинні перекривати операторів.

Як перевірити, чи щось є ПОД? Що ж, існує структура, яка називається 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, однак це слід робити лише для ImplementationInheritance (повторне використання коду), а не поліморфізм / підтипізація.

Поширене (хоча і не зовсім правильне) визначення полягає в тому, що тип PlainOldData - це все, що не має VeeTable.


Ваша відповідь дуже хороша, але це питання прийняло відповідь 8 років тому, а також кілька інших хороших відповідей. Ви можете більше внести свій внесок у ТО, якщо використовуєте свої знання для відповіді на запитання, на які ще не відповіли)))
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 відкрив речі досить небагато, дозволивши користувачеві знову представити вбудовані конструктори.


8

Приклади всіх випадків, що не static_assertстосуються POD, з ефектами від 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 ++, зазначені на веб- сайті: /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.


4

Поняття POD та типізований тип std::is_podбуде застарілим у C ++ 20. Див. Це запитання для отримання додаткової інформації.


-7

З допомогою C ++ "Звичайні старі дані" не означають, що такі речі, як int, char і т. Д., - єдині використовувані типи. Звичайні старі дані насправді означають, що ви можете перенести структуру memcpy з одного місця в пам'яті до іншого, і все буде працювати точно так, як ви очікували (тобто не підірвати). Це руйнується, якщо ваш клас або будь-який клас, який містить ваш клас, як член є вказівником або посиланням або класом, який має віртуальну функцію. По суті, якщо покажчики мають бути десь задіяні, це не звичайні старі дані.


6
Показники дозволені в структурах POD. Посилання не є.
j_random_hacker

1
Пасант тут відсутній.
icbytes
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.