web-dev-qa-db-ja.com

C++のPOD型は何ですか?

私はこのPODタイプという用語に数回出会いました。どういう意味ですか?

860
paxos1977

PODPlain Old Dataを表します。つまり、コンストラクタ、デストラクタ、および仮想メンバ関数を持たないクラス(キーワードstructまたはキーワードclassのどちらで定義されているかにかかわらず)です。 PODに関するウィキペディアの記事 はもう少し詳しく説明し、次のように定義しています:

C++のPlain Old Data Structureは、PODSのみをメンバとして含み、ユーザ定義のデストラクタ、ユーザ定義のコピー代入演算子、メンバへのポインタ型の非静的メンバを持たない集約クラスです。

より詳細な情報は C++ 98/03への回答 で見つけることができます。 C++ 11はPODを取り巻く規則を変更し、それらを大幅に緩和したので、 ここでフォローアップの回答が必要 となりました。

616
Greg Hewgill

非常に非公式に:

PODは、(クラスを含む)C++コンパイラが構造内で「魔法」が起こらないことを保証する型です。たとえば、vtablesへの隠されたポインタ、他の型にキャストされるときにアドレスに適用されるオフセットなどです。少なくともターゲットのPODもそうであれば)、コンストラクタ、またはデストラクタ。大まかに言って、その中の唯一のものが組み込み型とそれらの組み合わせである場合、型はPODです。結果はC型のように振舞います。

あまり非公式に:

  • intcharwchar_tboolfloatdoubleは、それらのlong/shortおよびsigned/unsignedバージョンと同様にPODです。
  • ポインター(関数へのポインターおよびメンバーへのポインターを含む)はPODです。
  • enumsはPODです
  • constまたはvolatile PODはPODです。
  • pODのclassstructまたはunionは、すべての非静的データメンバがpublicであり、基本クラスも、コンストラクタ、デストラクタ、または仮想メソッドも持たないという条件で、PODです。静的メンバーは、この規則の下で何かが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構造体は、非POD構造体、非POD共用体(またはそのような型の配列)、または参照の非静的データメンバーを持たない集約クラスです。同様に、POD共用体は、型がnon-POD-struct、non-POD-union(またはその配列)の非静的データメンバーを持たない集約共用体です。そのような型)または参照、およびユーザー定義のコピー演算子もユーザー定義のデストラクタもありません。

8.5.1(1): "集約は、ユーザ宣言コンストラクタ(12.1)、非公開または保護された非静的データメンバ(節11)を持たない配列またはクラス(節9)、no基本クラス(10章)で仮想関数はない(10.3)。 "

316
Steve Jessop

普通の古いデータ

つまり、すべての組み込みデータ型(例:intcharfloatlongunsigned chardoubleなど)およびすべてのPODデータの集約です。はい、それは再帰的な定義です。 ;)

より明確に言うと、PODは私たちが「構造体」と呼ぶものです。つまり、データを格納するだけのユニットまたはユニットのグループです。

17
ugasoft

私が理解しているように、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から)


参照:

POD(plain old data)オブジェクトは、これらのデータ型の1つ - 基本型、ポインタ、共用体、構造体、配列、またはクラス - を持ち、コンストラクタはありません。逆に、非PODオブジェクトはコンストラクタが存在するオブジェクトです。 PODオブジェクトは、そのタイプに適したサイズのストレージを取得した時点で存続期間を開始し、その存続期間はオブジェクトのストレージが再利用されるか割り当て解除された時点で終了します。

PlainOldData型には、次のものも含めないでください。

  • 仮想機能(独自の機能または継承された機能)
  • 仮想基本クラス(直接または間接).

PlainOldDataのよりゆるい定義は、コンストラクタを持つオブジェクトを含みます。しかし、仮想的なものを持つものは除外されます。 PlainOldData型に関する重要な問題は、それらが非多態性であるということです。継承はPOD型で実行できますが、ImplementationInheritance(コードの再利用)に対してのみ実行し、多態性/サブタイプ化に対しては実行しないでください。

(厳密ではないが)一般的な定義は、PlainOldData型がVeeTableを持たないものなら何でもということです。

8
amitabes

C++ 11からC++ 17までのstatic_assertを含むすべての非PODケースとPOD効果の例

std::is_podはC++ 11で追加されたので、とりあえずその標準以降を考えてみましょう。

https://stackoverflow.com/a/48435532/895245 に記載されているように、std::is_podはC++ 20から削除されます。サポートの代替品が届いたら、これを更新しましょう。

POD制限は標準が進化するにつれてますます緩和されてきました。私はifdefを通して例の中のすべての緩和をカバーすることを目指しています。

libstdc ++は、次の場所でわずかなテストを行っています。 https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc しかしそれは少なすぎます。メンテナ:この記事を読んだ場合はこれをマージしてください。 https://softwareengineering.stackexchange.com/questions/199708/is-there-a-compliance-test-for-c-compilers に記載されているすべてのC++ testsuiteプロジェクトをチェックアウトするのが面倒です。

#include <type_traits>
#include <array>
#include <vector>

int main() {
#if __cplusplus >= 201103L
    // # Not POD
    //
    // Non-POD examples. Let's just walk all non-recursive non-POD branches of cppreference.
    {
        // Non-trivial implies non-POD.
        // https://en.cppreference.com/w/cpp/named_req/TrivialType
        {
            // Has one or more default constructors, all of which are either
            // trivial or deleted, and at least one of which is not deleted.
            {
                // Not trivial because we removed the default constructor
                // by using our own custom non-default constructor.
                {
                    struct C {
                        C(int) {}
                    };
                    static_assert(std::is_trivially_copyable<C>(), "");
                    static_assert(!std::is_trivial<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }

                // No, this is not a default trivial constructor either:
                // https://en.cppreference.com/w/cpp/language/default_constructor
                //
                // The constructor is not user-provided (i.e., is implicitly-defined or
                // defaulted on its first declaration)
                {
                    struct C {
                        C() {}
                    };
                    static_assert(std::is_trivially_copyable<C>(), "");
                    static_assert(!std::is_trivial<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }
            }

            // Not trivial because not trivially copyable.
            {
                struct C {
                    C(C&) {}
                };
                static_assert(!std::is_trivially_copyable<C>(), "");
                static_assert(!std::is_trivial<C>(), "");
                static_assert(!std::is_pod<C>(), "");
            }
        }

        // Non-standard layout implies non-POD.
        // https://en.cppreference.com/w/cpp/named_req/StandardLayoutType
        {
            // Non static members with different access control.
            {
                // i is public and j is private.
                {
                    struct C {
                        public:
                            int i;
                        private:
                            int j;
                    };
                    static_assert(!std::is_standard_layout<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }

                // These have the same access control.
                {
                    struct C {
                        private:
                            int i;
                            int j;
                    };
                    static_assert(std::is_standard_layout<C>(), "");
                    static_assert(std::is_pod<C>(), "");

                    struct D {
                        public:
                            int i;
                            int j;
                    };
                    static_assert(std::is_standard_layout<D>(), "");
                    static_assert(std::is_pod<D>(), "");
                }
            }

            // Virtual function.
            {
                struct C {
                    virtual void f() = 0;
                };
                static_assert(!std::is_standard_layout<C>(), "");
                static_assert(!std::is_pod<C>(), "");
            }

            // Non-static member that is reference.
            {
                struct C {
                    int &i;
                };
                static_assert(!std::is_standard_layout<C>(), "");
                static_assert(!std::is_pod<C>(), "");
            }

            // Neither:
            //
            // - has no base classes with non-static data members, or
            // - has no non-static data members in the most derived class
            //   and at most one base class with non-static data members
            {
                // Non POD because has two base classes with non-static data members.
                {
                    struct Base1 {
                        int i;
                    };
                    struct Base2 {
                        int j;
                    };
                    struct C : Base1, Base2 {};
                    static_assert(!std::is_standard_layout<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }

                // POD: has just one base class with non-static member.
                {
                    struct Base1 {
                        int i;
                    };
                    struct C : Base1 {};
                    static_assert(std::is_standard_layout<C>(), "");
                    static_assert(std::is_pod<C>(), "");
                }

                // Just one base class with non-static member: Base1, Base2 has none.
                {
                    struct Base1 {
                        int i;
                    };
                    struct Base2 {};
                    struct C : Base1, Base2 {};
                    static_assert(std::is_standard_layout<C>(), "");
                    static_assert(std::is_pod<C>(), "");
                }
            }

            // Base classes of the same type as the first non-static data member.
            // TODO failing on GCC 8.1 -std=c++11, 14 and 17.
            {
                struct C {};
                struct D : C {
                    C c;
                };
                //static_assert(!std::is_standard_layout<C>(), "");
                //static_assert(!std::is_pod<C>(), "");
            };

            // C++14 standard layout new rules, yay!
            {
                // Has two (possibly indirect) base class subobjects of the same type.
                // Here C has two base classes which are indirectly "Base".
                //
                // TODO failing on GCC 8.1 -std=c++11, 14 and 17.
                // even though the example was copy pasted from cppreference.
                {
                    struct Q {};
                    struct S : Q { };
                    struct T : Q { };
                    struct U : S, T { };  // not a standard-layout class: two base class subobjects of type Q
                    //static_assert(!std::is_standard_layout<U>(), "");
                    //static_assert(!std::is_pod<U>(), "");
                }

                // Has all non-static data members and bit-fields declared in the same class
                // (either all in the derived or all in some base).
                {
                    struct Base { int i; };
                    struct Middle : Base {};
                    struct C : Middle { int j; };
                    static_assert(!std::is_standard_layout<C>(), "");
                    static_assert(!std::is_pod<C>(), "");
                }

                // None of the base class subobjects has the same type as
                // for non-union types, as the first non-static data member
                //
                // TODO: similar to the C++11 for which we could not make a proper example,
                // but with recursivity added.

                // TODO come up with an example that is POD in C++14 but not in C++11.
            }
        }
    }

    // # POD
    //
    // POD examples. Everything that does not fall neatly in the non-POD examples.
    {
        // Can't get more POD than this.
        {
            struct C {};
            static_assert(std::is_pod<C>(), "");
            static_assert(std::is_pod<int>(), "");
        }

        // Array of POD is POD.
        {
            struct C {};
            static_assert(std::is_pod<C>(), "");
            static_assert(std::is_pod<C[]>(), "");
        }

        // Private member: became POD in C++11
        // https://stackoverflow.com/questions/4762788/can-a-class-with-all-private-members-be-a-pod-class/4762944#4762944
        {
            struct C {
                private:
                    int i;
            };
#if __cplusplus >= 201103L
            static_assert(std::is_pod<C>(), "");
#else
            static_assert(!std::is_pod<C>(), "");
#endif
        }

        // Most standard library containers are not POD because they are not trivial,
        // which can be seen directly from their interface definition in the standard.
        // https://stackoverflow.com/questions/27165436/pod-implications-for-a-struct-which-holds-an-standard-library-container
        {
            static_assert(!std::is_pod<std::vector<int>>(), "");
            static_assert(!std::is_trivially_copyable<std::vector<int>>(), "");
            // Some might be though:
            // https://stackoverflow.com/questions/3674247/is-stdarrayt-s-guaranteed-to-be-pod-if-t-is-pod
            static_assert(std::is_pod<std::array<int, 1>>(), "");
        }
    }

    // # POD effects
    //
    // Now let's verify what effects does PODness have.
    //
    // Note that this is not easy to do automatically, since many of the
    // failures are undefined behaviour.
    //
    // A good initial list can be found at:
    // https://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special/4178176#4178176
    {
        struct Pod {
            uint32_t i;
            uint64_t j;
        };
        static_assert(std::is_pod<Pod>(), "");

        struct NotPod {
            NotPod(uint32_t i, uint64_t j) : i(i), j(j) {}
            uint32_t i;
            uint64_t j;
        };
        static_assert(!std::is_pod<NotPod>(), "");

        // __attribute__((packed)) only works for POD, and is ignored for non-POD, and emits a warning
        // https://stackoverflow.com/questions/35152877/ignoring-packed-attribute-because-of-unpacked-non-pod-field/52986680#52986680
        {
            struct C {
                int i;
            };

            struct D : C {
                int j;
            };

            struct E {
                D d;
            } /*__attribute__((packed))*/;

            static_assert(std::is_pod<C>(), "");
            static_assert(!std::is_pod<D>(), "");
            static_assert(!std::is_pod<E>(), "");
        }
    }
#endif
}

GitHubアップストリーム

でテスト済み:

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上。

PODと型特性std::is_podの概念はC++ 20で廃止予定です。さらなる情報については this questionを参照してください。

3
ThomasMcLeod

PODと非PODを区別する必要があるのはなぜですか。

C++はCの拡張としてその生涯を始めました。現代のC++はもはやCの厳密なスーパーセットではありませんが、人々は2つの間の高レベルの互換性をまだ期待しています。

大まかに言って、PODタイプはCと互換性があるタイプであり、おそらく同様に重要なことに特定のABI最適化と互換性があります。

Cと互換性を保つためには、2つの制約を満たす必要があります。

  1. レイアウトは対応するCタイプと同じでなければなりません。
  2. 型は、対応するCの型と同じ方法で関数との間でやり取りする必要があります。

特定のC++機能はこれと互換性がありません。

仮想メソッドは、仮想メソッドテーブルへの1つ以上のポインタを挿入することをコンパイラに要求します。これはCには存在しません。

ユーザー定義のコピーコンストラクタ、移動コンストラクタ、コピー代入、およびデストラクタは、パラメータの受け渡しに影響します。多くのC ABIは小さなパラメータをレジスタに渡したり返したりしますが、ユーザ定義のコンストラクタ/割り当て/デストラクタに渡された参照はメモリの場所でのみ機能します。

そのため、どのタイプが「C互換」であることが期待でき、どのタイプが不可能であるかを定義する必要があります。 C++ 03はこの点でやや厳しすぎました。 C++ 11はかなり物事を切り開いた。

1
plugwash