Jakie są typy POD w C ++?

977

Kilka razy spotkałem się z tym terminem.
Co to znaczy?

paxos1977
źródło
5
zobacz chat.stackoverflow.com/transcript/message/213026#213026 i wiadomości z następnego dnia, aby omówić zaakceptowaną odpowiedź
Johannes Schaub - litb
2
Również stackoverflow.com/questions/4178175/…
Mihran Hovsepyan
@ paxos1977: Zmień wybór „rozwiązania” (obecnie odpowiedź Hewgilla), aby zasadniczo zła odpowiedź nie wprowadzała w błąd pracowników Google, którzy tu trafią.
Pozdrawiam i hth. - Alf
Doszliśmy do wniosku, że łańcuch typu C NIE JEST typem POD, ponieważ 1.) wskaźnik nie sąsiaduje z danymi ciągu, oraz 2.) aby utworzyć ciąg typu POD, należy upewnić się, że typ miał znak zerowy w obrębie określonego rozmiaru typu POD, co prowadzi do niezdefiniowanego zachowania.

Odpowiedzi:

694

POD oznacza Plain Old Data - czyli klasę (zdefiniowaną za pomocą słowa kluczowego structlub słowa kluczowego class) bez funkcji konstruktorów, destruktorów i elementów wirtualnych. Artykuł Wikipedii na temat POD jest nieco bardziej szczegółowy i definiuje go jako:

Zwykła stara struktura danych w C ++ to klasa agregująca, która zawiera tylko elementy PODS jako elementy, nie ma niszczyciela zdefiniowanego przez użytkownika, żadnego operatora przypisania kopii zdefiniowanego przez użytkownika ani żadnych niestatycznych elementów typu wskaźnik do elementu.

Więcej szczegółów można znaleźć w tej odpowiedzi dla C ++ 98/03 . C ++ 11 zmienił zasady dotyczące POD, znacznie je rozluźniając, wymagając w związku z tym odpowiedzi uzupełniającej .

Greg Hewgill
źródło
34
Jest różnica. Typy wewnętrzne są prymitywami języka „wbudowanymi”. Są to typy POD oraz ich agregacje (i inne POD).
Adam Wright
59
Typy POD mają cechy, których nie mają typy inne niż POD. Na przykład, jeśli masz globalną, stałą strukturę typu POD, możesz zainicjować jej zawartość za pomocą notacji nawiasowej, jest ona umieszczana w pamięci tylko do odczytu i nie trzeba generować kodu, aby ją zainicjować (konstruktor lub w inny sposób), ponieważ jest to część obrazu programu. Jest to ważne dla osób osadzonych, które często mają ścisłe ograniczenia dotyczące pamięci RAM, ROM lub Flash.
Mike DeSimone
34
W C ++ 11 możesz użyć std :: is_pod <MyType> (), aby stwierdzić, czy MyType jest POD.
allyourcode
7
Raport techniczny Bjarne Stroustrup dotyczący wydajności C ++ stwierdza, że ​​standard C ++ opisuje POD jako „ typ danych, który jest zgodny z równoważnym typem danych w C pod względem układu, inicjalizacji i możliwości kopiowania za pomocą memcpy ”. Być może należy wprowadzić rozróżnienie między typem POD a strukturą POD.
user34660,
6
−1 Ta odpowiedź jest nadal zasadniczo błędna i wprowadza w błąd z dniem 16 sierpnia 2016 r .: typy POD nie są ograniczone do typów klas.
Pozdrawiam i hth. - Alf
352

Bardzo nieformalnie:

POD jest typem (w tym klasami), w którym kompilator C ++ gwarantuje, że w strukturze nie będzie „magii”: na przykład ukryte wskaźniki do tabel vt, przesunięcia, które są stosowane do adresu, gdy jest rzutowany na inne typy ( przynajmniej jeśli POD jest również celem), konstruktorów lub destruktorów. Z grubsza mówiąc, typ jest POD, gdy jedyne w nim są wbudowane typy i ich kombinacje. Rezultatem jest coś, co „działa jak” typ C.

Mniej nieformalnie:

  • int, char, wchar_t, bool, float, doubleSą POD, podobnie jak long/shorti signed/unsignedwersje nich.
  • wskaźniki (w tym wskaźnik do funkcji i wskaźnik do elementu) są POD,
  • enums są POD
  • a constlub volatilePOD jest POD.
  • a class, structlub unionPOD jest POD, pod warunkiem, że wszystkie niestatyczne elementy danych są public, i nie ma żadnej klasy bazowej ani żadnych konstruktorów, destruktorów ani metod wirtualnych. Członkowie statyczni nie zatrzymują, aby coś było POD jako podstawa tej zasady. Ta reguła uległa zmianie w C ++ 11 i niektórzy członkowie prywatni są dozwoleni: czy klasa ze wszystkimi członkami prywatnymi może być klasą POD?
  • Wikipedia błędnie twierdzi, że POD nie może mieć członków typu wskaźnik do członka. A raczej poprawne jest sformułowanie w C ++ 98, ale TC1 wyraźnie powiedział, że wskaźniki do członka są POD.

Formalnie (C ++ 03 Standard):

3.9 (10): „Typy arytmetyczne (3.9.1), typy wyliczeń, typy wskaźników i wskaźnik do typów elementów (3.9.2) i wersje tych typów zakwalifikowane do CV (3.9.3) są zbiorowymi typami skalarów wywołujących. Skalarne typy, typy struktur POD, typy związków POD (klauzula 9), tablice takich typów i wersje tych typów zakwalifikowane do CV (3.9.3) są wspólnie nazywane typami POD ”

9 (4): „Struktura POD jest strukturą zagregowaną, która nie ma elementów danych niestatycznych typu non-POD-struct, non-POD-union (lub tablica takich typów) lub referencji i nie ma użytkownika zdefiniuj operator kopiowania i brak niszczyciela zdefiniowanego przez użytkownika. Podobnie związek POD jest związkiem agregowanym, który nie ma elementów danych niestatycznych typu non-POD-struct, non-POD-union (lub tablica takich typów) lub odniesienia, i nie ma operatora kopiowania zdefiniowanego przez użytkownika ani destruktora zdefiniowanego przez użytkownika.

8.5.1 (1): „Agregacja to tablica lub klasa (klauzula 9) bez konstruktorów zadeklarowanych przez użytkownika (12.1), bez prywatnych lub chronionych niestatycznych elementów danych (klauzula 11), bez klas podstawowych (klauzula 10) i brak funkcji wirtualnych (10.3). ”

Steve Jessop
źródło
3
Masz formalny / mniej formalny. Możesz dodać ogólną zasadę. Typy wbudowane i agregacje typów wbudowanych (lub coś w tym rodzaju). Oprócz dokładnej definicji musimy uczynić tę wiedzę łatwą w użyciu.
Martin York,
1
Nieco się mylisz przy bicie „offsetów, gdy cast_to innego typu”. Te przesunięcia są stosowane podczas rzutowania na klasę bazową lub pochodną. Tak więc, jeśli rzutujesz ze wskaźnika klasy podstawowej POD na klasę inną niż POD, nadal możesz napotkać korektę.
MSalters
1
@ Steve Jessop: Dlaczego w ogóle musimy rozróżniać POD i inne?
Lazer,
6
@Lazer: to zupełnie inne pytanie: „jak zachowują się POD?” w przeciwieństwie do „co oznacza POD?”. Podsumowując, różnica dotyczy inicjalizacji (stąd też użycia memcpy do powielania obiektów), kompatybilności z układem struktury C dla tego kompilatora oraz up-down-castingu wskaźnika. Urządzenia POD „działają jak typy C”, nie gwarantuje się, że urządzenia POD nie będą tego robić. Więc jeśli chcesz, aby Twój typ działał przenośnie jak struktura C, musisz upewnić się, że jest to POD, więc musisz znać różnicę.
Steve Jessop,
4
@muntoo: tak, naprawdę komentowałem odpowiedź, która cytuje nieaktualne informacje z Wikipedii. Mógłbym zredagować tę odpowiedź, ale chyba czuję kłopoty, jeśli zajmuję się edytowaniem odpowiedzi innych ludzi, aby zgodzić się z moją, bez względu na to, jak słusznie uważam.
Steve Jessop,
20

Zwykłe stare dane

W skrócie, to wszystko jest wbudowane typy danych (na przykład int, char, float, long, unsigned char, double, itd.) I wszystkie agregacji danych POD. Tak, to definicja rekurencyjna. ;)

Aby być bardziej zrozumiałym, POD jest tym, co nazywamy „struct”: jednostka lub grupa jednostek, które tylko przechowują dane.

ugasoft
źródło
12
To prawda, że ​​czasami nazywamy ich „strukturą”. Jednak zawsze się mylimy, ponieważ struct niekoniecznie jest typem POD.
Steve Jessop
6
oczywiście ... struct i klasa są prawie równoważne, ale w „biznesie” nazywamy „struct” prostym urządzeniem do gromadzenia danych, zwykle bez liczników i dtor, zwykle z semantyką wartości ...
ugasoft
2
Dla mnie błędem C ++ było uczynienie struct identycznym ze słowem kluczowym class lub zbliżonym do: struct tylko dodaje publiczny domyślny dostęp do klasy. Łatwiej było tworzyć struktury podobne do C i mielibyśmy POD w dniu 0 c ++.
user1708042
ugasoft: twoja odpowiedź może wprowadzać w błąd - twój komentarz wyjaśnił brakujący szczegół, że jest tak stosowany w praktyce, a nie w standardzie. Whoa, 8 lat, w ogóle tu jesteś? ;-)
hauron
Z wyjątkiem łańcucha, ponieważ nie można go skopiować za pomocą memcpy bez uprzedniego określenia długości łańcucha.
12

Jak rozumiem POD (PlainOldData) to tylko surowe dane - nie potrzebuje:

  • być zbudowanym,
  • być zniszczonym,
  • mieć niestandardowe operatory.
  • Nie może mieć funkcji wirtualnych,
  • i nie może zastępować operatorów.

Jak sprawdzić, czy coś jest POD? Istnieje struktura, która nazywa się 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)>
  { };
}

(Z nagłówka type_traits)


Odniesienie:

набиячлэвэли
źródło
2
Niepoprawnie, typ POD może mieć funkcje składowe lub przeciążonych operatorów. (Ale może nie mieć wirtualnych funkcji członkowskich.)
Colin D Bennett
@ColinDBennett Tak, to prawda. Przepraszam za zamieszanie. Edytowane w / poza anwser.
набиячлэвэли
10

Obiekt POD (zwykłe stare dane) ma jeden z tych typów danych - typ podstawowy, wskaźnik, związek, strukturę, tablicę lub klasę - bez konstruktora. I odwrotnie, obiekt niebędący POD jest obiektem, dla którego istnieje konstruktor. Żywotność obiektu POD rozpoczyna się, gdy uzyskuje miejsce do przechowywania o rozmiarze odpowiednim dla swojego typu, a okres jego użytkowania kończy się, gdy miejsce dla obiektu zostanie ponownie wykorzystane lub zwolnione.

Typy PlainOldData również nie mogą mieć żadnego z:

  • Funkcje wirtualne (własne lub odziedziczone)
  • Wirtualne klasy bazowe (bezpośrednie lub pośrednie).

Luźniejsza definicja PlainOldData obejmuje obiekty z konstruktorami; ale wyklucza osoby posiadające cokolwiek wirtualnego. Ważnym problemem związanym z typami PlainOldData jest to, że nie są one polimorficzne. Dziedziczenie można wykonać z typami POD, jednak należy to zrobić tylko dla ImplementationInheritance (ponowne użycie kodu), a nie polimorfizm / podtyp.

Częstą (choć nie do końca poprawną) definicją jest to, że typ PlainOldData to wszystko, co nie ma VeeTable.

amitabes
źródło
Twoja odpowiedź jest bardzo dobra, ale na to pytanie przyjęto odpowiedź 8 lat temu oraz kilka innych dobrych odpowiedzi. Możesz wnieść większy wkład w SO, jeśli wykorzystasz swoją wiedzę, aby odpowiedzieć na pytania, na które jeszcze nie ma odpowiedzi)))
mvidelgauz 16.08.16
10

Dlaczego w ogóle musimy rozróżniać POD i POD-POD?

C ++ zaczęło swoje życie jako rozszerzenie C. Chociaż współczesne C ++ nie jest już ścisłym nadzorem C, ludzie nadal oczekują wysokiego poziomu kompatybilności między nimi.

Z grubsza mówiąc, typ POD jest typem kompatybilnym z C i być może równie ważnym jest kompatybilny z niektórymi optymalizacjami ABI.

Aby być kompatybilnym z C, musimy spełnić dwa ograniczenia.

  1. Układ musi być taki sam jak odpowiedni typ C.
  2. Typ musi zostać przekazany i zwrócony z funkcji w taki sam sposób, jak odpowiedni typ C.

Niektóre funkcje C ++ są z tym niezgodne.

Metody wirtualne wymagają, aby kompilator wstawił jeden lub więcej wskaźników do tabel metod wirtualnych, co nie istnieje w C.

Zdefiniowane przez użytkownika konstruktory kopiowania, konstruktory przenoszenia, przypisania kopii i destruktory mają wpływ na przekazywanie i zwracanie parametrów. Wiele C ABI przekazuje i zwraca małe parametry w rejestrach, ale odniesienia przekazywane do zdefiniowanego przez użytkownika konstruktora / przypisania / destruktora mogą działać tylko z lokalizacjami pamięci.

Konieczne jest więc zdefiniowanie, jakie typy mogą być „kompatybilne z C”, a jakie nie. C ++ 03 był nieco zbyt rygorystyczny pod tym względem, każdy konstruktor zdefiniowany przez użytkownika wyłączyłby wbudowane konstruktory, a każda próba dodania ich z powrotem spowodowałaby, że byłyby zdefiniowane przez użytkownika, a zatem typ nie byłby pod. C ++ 11 całkiem sporo otworzył, umożliwiając użytkownikowi ponowne wprowadzenie wbudowanych konstruktorów.

płyn do płukania
źródło
8

Przykłady wszystkich przypadków innych niż POD z static_assertefektami od C ++ 11 do C ++ 17 i POD

std::is_pod został dodany w C ++ 11, więc na razie rozważmy ten standard.

std::is_podzostanie usunięty z C ++ 20, jak wspomniano na https://stackoverflow.com/a/48435532/895245 , zaktualizujmy to, gdy pojawi się wsparcie dla zamienników.

Ograniczenia POD stają się coraz bardziej rozluźnione w miarę rozwoju standardu, staram się objąć wszystkie relaksacje w tym przykładzie poprzez ifdefs.

libstdc ++ ma trochę testów na: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc, ale to po prostu za mało. Opiekunowie: połącz to, jeśli czytasz ten post. Jestem leniwy, by sprawdzić wszystkie projekty testów C ++ wspomniane na stronie : /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 w górę .

Testowane z:

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

na Ubuntu 18.04, GCC 8.2.0.

Ciro Santilli
źródło
4

Pojęcie POD i cechy typu std::is_podbędzie przestarzałe w C ++ 20. Zobacz to pytanie, aby uzyskać dodatkowe informacje.

ThomasMcLeod
źródło
-7

W C ++ Plain Old Data nie oznacza tylko, że jedynymi używanymi typami są int, char, itp. Zwykłe stare dane naprawdę oznaczają w praktyce, że możesz przenieść struct memcpy z jednej lokalizacji w pamięci do drugiej i wszystko będzie działało dokładnie tak, jak byś się spodziewał (tj. Nie wybuchł). To się psuje, jeśli twoja klasa lub jakakolwiek klasa, którą zawiera twoja klasa, ma jako element członkowski, który jest wskaźnikiem, referencją lub klasą, która ma funkcję wirtualną. Zasadniczo, jeśli wskaźniki muszą być gdzieś zaangażowane, nie są to zwykłe stare dane.

Mark Kegel
źródło
6
Wskaźniki są dozwolone w strukturach POD. Referencje nie są.
j_random_hacker
1
Brakuje tutaj Passanta.
icbytes