Apa jenis POD dalam C ++?

978

Saya telah menemukan istilah ini tipe POD beberapa kali.
Apa artinya?

paxos1977
sumber
5
silakan lihat chat.stackoverflow.com/transcript/message/213026#213026 dan pesan hari berikutnya untuk diskusi tentang jawaban yang diterima
Johannes Schaub - litb
2
Juga stackoverflow.com/questions/4178175/…
Mihran Hovsepyan
@ paxos1977: Silakan ubah pilihan "solusi" Anda (saat ini jawaban Hewgill) sehingga jawaban yang salah secara mendasar tidak menyesatkan para googler yang berakhir di sini.
Ceria dan hth. - Alf
Kami telah menyimpulkan bahwa string c-style BUKAN tipe POD karena 1.) pointer tidak berdekatan dengan data string, dan 2.) untuk membuat string menjadi tipe POD, Anda perlu memastikan jenisnya memiliki karakter nil-term di dalamnya dalam ukuran yang telah ditentukan dari jenis POD, yang mengarah ke perilaku yang tidak ditentukan.

Jawaban:

695

POD adalah singkatan dari Plain Old Data - yaitu, kelas (baik didefinisikan dengan kata kunci structatau kata kunci class) tanpa fungsi konstruktor, destruktor dan anggota virtual. Artikel Wikipedia tentang POD masuk ke sedikit lebih detail dan mendefinisikannya sebagai:

Struktur Data Lama Biasa dalam C ++ adalah kelas agregat yang hanya berisi PODS sebagai anggota, tidak memiliki destruktor yang ditentukan pengguna, tidak ada operator penugasan salinan yang ditentukan pengguna, dan tidak ada anggota tipe pointer-ke-anggota yang tidak statis.

Detail lebih lanjut dapat ditemukan dalam jawaban ini untuk C ++ 98/03 . C ++ 11 mengubah aturan seputar POD, membuat mereka sangat rileks, sehingga memerlukan jawaban tindak lanjut di sini .

Greg Hewgill
sumber
35
Ada perbedaan. Tipe intrinsik adalah primitif bahasa "builtin". Jenis POD adalah ini, ditambah agregasi dari ini (dan POD lainnya).
Adam Wright
59
Jenis POD memiliki karakteristik yang bukan tipe POD. Misalnya, jika Anda memiliki global, const, struct tipe POD, Anda dapat menginisialisasi isinya dengan notasi brace, ia dimasukkan ke dalam memori read-only, dan tidak ada kode yang perlu dihasilkan untuk menginisialisasi (konstruktor atau lainnya), karena itu bagian dari gambar program. Ini penting untuk orang yang disematkan yang sering memiliki keterbatasan pada RAM, ROM, atau Flash.
Mike DeSimone
35
Di C ++ 11, Anda dapat melakukan std :: is_pod <MyType> () untuk mengetahui apakah MyType adalah POD.
allyourcode
7
Laporan Teknis Bjarne Stroustrup tentang Kinerja C ++ menyatakan bahwa standar C ++ menggambarkan POD sebagai " tipe data yang kompatibel dengan tipe data yang setara dalam tata letak C, inisialisasi, dan kemampuannya untuk disalin dengan memcpy ". Mungkin perbedaan harus dibuat antara tipe POD dan struktur POD.
user34660
6
−1 Jawaban ini pada dasarnya masih salah & menyesatkan pada 16 Agustus 2016: tipe POD tidak terbatas pada tipe kelas.
Ceria dan hth. - Alf
353

Sangat informal:

POD adalah tipe (termasuk kelas) di mana kompiler C ++ menjamin bahwa tidak akan ada "keajaiban" yang terjadi dalam struktur: misalnya pointer tersembunyi ke vtables, offset yang diterapkan ke alamat ketika dilemparkan ke tipe lain ( setidaknya jika POD target juga), konstruktor, atau destruktor. Secara kasar, suatu tipe adalah POD ketika satu-satunya yang ada di dalamnya adalah tipe dan kombinasi bawaannya. Hasilnya adalah sesuatu yang "bertindak seperti" tipe C.

Kurang informal:

  • int, char, wchar_t, bool, float, doubleYang PODS, seperti long/shortdan signed/unsignedversi mereka.
  • pointer (termasuk pointer-to-function dan pointer-to-member) adalah POD,
  • enums adalah POD
  • a constatau volatilePOD adalah POD.
  • a class, structatau unionPOD adalah POD dengan ketentuan bahwa semua anggota data non-statis adalah public, dan tidak memiliki kelas dasar dan tidak ada konstruktor, destruktor, atau metode virtual. Anggota statis tidak menghentikan sesuatu menjadi POD di bawah aturan ini. Aturan ini telah berubah di C ++ 11 dan anggota pribadi tertentu diperbolehkan: Bisakah kelas dengan semua anggota pribadi menjadi kelas POD?
  • Wikipedia salah mengatakan bahwa POD tidak dapat memiliki anggota tipe pointer-to-member. Atau lebih tepatnya, itu benar untuk kata-kata C ++ 98, tetapi TC1 membuat eksplisit bahwa pointer-ke-anggota adalah POD.

Secara formal (Standar C ++ 03):

3.9 (10): "Tipe aritmatika (3.9.1), tipe enumerasi, tipe pointer, dan pointer ke tipe anggota (3.9.2) dan versi yang memenuhi syarat cv dari tipe ini (3.9.3) adalah tipe skalar pemanggil secara kolektif. Scalar tipe, tipe POD-struct, tipe POD-union (klausa 9), array tipe seperti itu dan versi cv-kualifikasi dari tipe-tipe ini (3.9.3) secara kolektif disebut tipe POD "

9 (4): "Sebuah POD-struct adalah kelas agregat yang tidak memiliki anggota data non-statis dari tipe non-POD-struct, non-POD-union (atau array jenis seperti itu) atau referensi, dan tidak memiliki pengguna- mendefinisikan operator salinan dan tidak ada destruktor yang ditentukan pengguna. Demikian pula serikat-POD adalah serikat agregat yang tidak memiliki anggota data non-statis dari tipe non-POD-struct, non-POD-union (atau array jenis seperti itu) atau referensi, dan tidak memiliki operator penyalin yang ditetapkan pengguna dan tidak ada destruktor yang ditentukan pengguna.

8.5.1 (1): "Agregat adalah array atau kelas (ayat 9) tanpa konstruktor yang dideklarasikan pengguna (12.1), tidak ada anggota data non-statis pribadi atau yang dilindungi (klausa 11), tidak ada kelas dasar (klausa 10) dan tidak ada fungsi virtual (10.3). "

Steve Jessop
sumber
3
Anda memiliki formal / kurang formal. Anda bisa menambahkan aturan praktis. Tipe bawaan dan agregasi tipe Built in (atau sesuatu seperti itu). Selain untuk mendapatkan definisi yang tepat, kita perlu membuat pengetahuan mudah digunakan.
Martin York
1
Anda sedikit salah pada bit "offset saat cast_to tipe lain". Offset tersebut diterapkan saat melakukan casting ke base atau kelas turunan. Jadi, jika Anda melemparkan dari pointer kelas basis POD ke kelas turunan non-POD, Anda mungkin masih mengalami penyesuaian.
MSalters
1
@Steve Jessop: Mengapa kita perlu membedakan antara POD dan non-POD sama sekali?
Lazer
6
@ Lazer: itu pertanyaan lain, "bagaimana POD berperilaku?" sebagai lawan dari "apa arti POD?". Singkatnya perbedaannya berkaitan dengan inisialisasi (karenanya juga menggunakan memcpy untuk menduplikasi objek), kompatibilitas dengan tata letak C struct untuk kompiler itu, dan penunjuk ke atas dan ke bawah casting. POD "bertindak seperti tipe C", non-POD tidak dijamin melakukannya. Jadi, jika Anda ingin tipe Anda bertindak seperti C struct, Anda harus memastikan bahwa itu adalah POD, jadi Anda perlu mengetahui perbedaannya.
Steve Jessop
4
@muntoo: sudah, benar-benar saya mengomentari jawaban yang mengutip info usang dari Wikipedia. Saya bisa mengedit jawaban itu, saya kira, tetapi saya mencium masalah jika saya berkeliling mengedit jawaban orang lain untuk setuju dengan saya, tidak peduli seberapa benar saya pikir saya.
Steve Jessop
21

Data Lama Biasa

Singkatnya, itu semua built-in tipe data (misalnya int, char, float, long, unsigned char, double, dll) dan semua agregasi data POD. Ya, itu definisi rekursif. ;)

Untuk lebih jelasnya, POD adalah apa yang kita sebut "struct": unit atau sekelompok unit yang hanya menyimpan data.

ugasoft
sumber
13
Memang benar bahwa kadang-kadang kita menyebutnya 'struct'. Namun kami selalu salah untuk melakukannya, karena struct tidak harus tipe POD.
Steve Jessop
7
jelas ... struct dan kelas hampir setara, tetapi dalam "bisnis" kami menyebutnya 'struct' sebagai pengumpul data sederhana, biasanya tanpa ctors dan dtor, biasanya dengan semantik nilai ...
ugasoft
2
Bagi saya itu adalah C ++ salah untuk membuat struct identik dengan kata kunci kelas atau dekat dengan: struct hanya menambahkan akses standar publik ke kelas. Saya lebih sederhana untuk membuat struct mirip C dan kami akan memiliki POD pada hari ke 0 dari c ++.
user1708042
ugasoft: jawaban Anda mungkin menyesatkan - komentar Anda menjelaskan detail yang hilang yang digunakan seperti itu dalam praktik, bukan standar. Whoa, 8 tahun, apakah Anda bahkan di sini? ;-)
hauron
Dengan pengecualian string karena Anda tidak dapat menyalinnya dengan memcpy tanpa terlebih dahulu menentukan panjang string.
12

Seperti yang saya mengerti POD (PlainOldData) hanyalah data mentah - tidak perlu:

  • untuk dibangun,
  • untuk dihancurkan,
  • untuk memiliki operator khusus.
  • Tidak boleh memiliki fungsi virtual,
  • dan tidak boleh mengesampingkan operator.

Bagaimana cara mengecek apakah ada POD? Nah, ada struct untuk yang disebut 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)>
  { };
}

(Dari header type_traits)


Referensi:

набиячлэвэли
sumber
2
Salah, tipe POD mungkin memiliki fungsi anggota atau operator yang kelebihan beban. (Tapi itu mungkin tidak memiliki fungsi anggota virtual.)
Colin D Bennett
@ColinDBennett Ya, itu benar. Maaf bila membingungkan. Diedit masuk / keluar dari server.
набиячлэвэли
10

Objek POD (data lama biasa) memiliki salah satu dari tipe data ini - tipe fundamental, pointer, union, struct, array, atau kelas - tanpa konstruktor. Sebaliknya, objek non-POD adalah objek yang memiliki konstruktor. Objek POD memulai masa pakainya ketika memperoleh penyimpanan dengan ukuran yang sesuai untuk jenisnya dan masa pakainya berakhir ketika penyimpanan untuk objek tersebut digunakan kembali atau tidak dialokasikan.

Jenis PlainOldData juga tidak boleh memiliki:

  • Fungsi virtual (baik milik mereka sendiri, atau yang diwarisi)
  • Kelas dasar virtual (langsung atau tidak langsung).

Definisi yang lebih longgar dari PlainOldData mencakup objek dengan konstruktor; tetapi mengecualikan mereka dengan apa pun virtual. Masalah penting dengan tipe PlainOldData adalah bahwa mereka non-polimorfik. Warisan dapat dilakukan dengan jenis POD, namun itu hanya boleh dilakukan untuk Implementasi Warisan (penggunaan kembali kode) dan bukan polimorfisme / subtipe.

Definisi umum (meskipun tidak sepenuhnya benar) adalah bahwa tipe PlainOldData adalah sesuatu yang tidak memiliki VeeTable.

amitabes
sumber
Jawaban Yuor sangat baik, tetapi pertanyaan ini telah menerima jawaban 8 tahun yang lalu, ditambah beberapa jawaban baik lainnya. Anda dapat berkontribusi lebih banyak ke SO jika Anda menggunakan pengetahuan Anda untuk menjawab pertanyaan yang belum dijawab)))
mvidelgauz
10

Mengapa kita perlu membedakan antara POD dan non-POD sama sekali?

C ++ memulai hidupnya sebagai perpanjangan dari C. Sementara C ++ modern bukan lagi superset C yang ketat, orang masih mengharapkan tingkat kompatibilitas yang tinggi di antara keduanya.

Secara kasar, tipe POD adalah tipe yang kompatibel dengan C dan mungkin sama pentingnya adalah kompatibel dengan optimisasi ABI tertentu.

Agar kompatibel dengan C, kita perlu memenuhi dua kendala.

  1. Tata letak harus sama dengan tipe C yang sesuai.
  2. Jenis harus diteruskan ke dan dikembalikan dari fungsi dengan cara yang sama seperti jenis C yang sesuai.

Fitur C ++ tertentu tidak kompatibel dengan ini.

Metode virtual memerlukan kompiler untuk memasukkan satu atau lebih petunjuk ke tabel metode virtual, sesuatu yang tidak ada di C.

Copy constructor yang ditentukan pengguna, memindahkan constructor, menyalin tugas dan destructor mempunyai implikasi untuk melewati dan mengembalikan parameter. Banyak C ABI lulus dan mengembalikan parameter kecil dalam register, tetapi referensi yang diteruskan ke konstruktor / assigment / destructor yang ditentukan pengguna hanya dapat bekerja dengan lokasi memori.

Jadi ada kebutuhan untuk menentukan tipe apa yang bisa diharapkan "kompatibel dengan C" dan tipe apa yang tidak bisa. C ++ 03 agak terlalu ketat dalam hal ini, setiap konstruktor yang ditentukan pengguna akan menonaktifkan konstruktor bawaan dan setiap upaya untuk menambahkannya kembali akan menghasilkan mereka yang ditetapkan pengguna dan karenanya jenisnya adalah non-pod. C ++ 11 membuka banyak hal, dengan memungkinkan pengguna untuk memperkenalkan kembali konstruktor bawaan.

plugwash
sumber
8

Contoh semua kasus non-POD dengan static_assertefek C ++ 11 hingga C ++ 17 dan POD

std::is_pod ditambahkan dalam C ++ 11, jadi mari kita pertimbangkan standar itu dan seterusnya untuk saat ini.

std::is_podakan dihapus dari C ++ 20 sebagaimana disebutkan di https://stackoverflow.com/a/48435532/895245 , mari perbarui ini saat dukungan datang untuk penggantian.

Pembatasan POD menjadi lebih dan lebih santai seiring standar berkembang, saya bertujuan untuk mencakup semua relaksasi dalam contoh melalui ifdefs.

libstdc ++ memiliki sedikit pengujian di: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc tetapi itu terlalu sedikit. Maintainers: tolong gabungkan ini jika Anda membaca posting ini. Saya malas untuk memeriksa semua proyek C ++ testuite yang disebutkan di: /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 hulu .

Diuji dengan:

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

pada Ubuntu 18.04, GCC 8.2.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
4

Konsep POD dan tipe sifat std::is_podakan ditinggalkan dalam C ++ 20. Lihat pertanyaan ini untuk informasi lebih lanjut.

ThomasMcLeod
sumber
-7

Dengan C ++, Plain Old Data tidak hanya berarti bahwa hal-hal seperti int, char, dll adalah satu-satunya tipe yang digunakan. Plain Old Data benar-benar berarti dalam praktiknya Anda dapat mengambil struct memcpy dari satu lokasi di memori ke yang lain dan segala sesuatunya akan bekerja persis seperti yang Anda harapkan (yaitu tidak meledak). Ini pecah jika kelas Anda, atau kelas mana pun yang berisi kelas Anda, memiliki sebagai anggota yang merupakan pointer atau referensi atau kelas yang memiliki fungsi virtual. Pada dasarnya, jika pointer harus terlibat di suatu tempat, itu bukan Data Lama Biasa.

Mark Kegel
sumber
6
Pointer diperbolehkan dalam POD struct. Referensi tidak.
j_random_hacker
1
Passant hilang di sini.
icbytes