我遇到过几次POD-type这个术语。 这是什么意思?


POD代表普通旧数据——也就是说,一个没有构造函数、析构函数和虚成员函数的类(无论是用关键字struct还是用关键字class定义的)。维基百科关于POD的文章更详细一些,并将其定义为:

c++中的普通旧数据结构是一个聚合类,它只包含PODS作为成员,没有用户定义的析构函数,没有用户定义的复制赋值操作符,也没有指针到成员类型的非静态成员。

更多的细节可以在c++ 98/03的答案中找到。c++ 11改变了围绕POD的规则,极大地放松了它们,因此需要在这里进行后续回答。


普通旧数据

简而言之,它是所有内置数据类型(例如int、char、float、long、unsigned char、double等)和POD数据的所有聚合。是的,这是递归定义。;)

更清楚地说,POD就是我们所说的“结构体”:仅存储数据的单元或单元组。


非常非正式地:

POD是一种类型(包括类),c++编译器保证结构中不会发生“魔法”:例如,指向虚表的隐藏指针,转换为其他类型时应用于地址的偏移量(至少如果目标也是POD),构造函数或析构函数。粗略地说,当类型中只有内置类型和它们的组合时,类型就是POD。结果是某种“像”C类型的东西。

非正式地:

int, char, wchar_t, bool, float, double are PODs, as are long/short and signed/unsigned versions of them. pointers (including pointer-to-function and pointer-to-member) are PODs, enums are PODs a const or volatile POD is a POD. a class, struct or union of PODs is a POD provided that all non-static data members are public, and it has no base class and no constructors, destructors, or virtual methods. Static members don't stop something being a POD under this rule. This rule has changed in C++11 and certain private members are allowed: Can a class with all private members be a POD class? Wikipedia is wrong to say that a POD cannot have members of type pointer-to-member. Or rather, it's correct for the C++98 wording, but TC1 made explicit that pointers-to-member are POD.

形式上(c++ 03标准):

3.9(10): "Arithmetic types (3.9.1), enumeration types, pointer types, and pointer to member types (3.9.2) and cv-qualified versions of these types (3.9.3) are collectively caller scalar types. Scalar types, POD-struct types, POD-union types (clause 9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called POD types" 9(4): "A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-define copy operator and no user-defined destructor. Similarly a POD-union is an aggregate union that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-define copy operator and no user-defined destructor. 8.5.1(1): "An aggregate is an array or class (clause 9) with no user-declared constructors (12.1), no private or protected non-static data members (clause 11), no base classes (clause 10) and no virtual functions (10.3)."


With C++, Plain Old Data doesn't just mean that things like int, char, etc are the only types used. Plain Old Data really means in practice that you can take a struct memcpy it from one location in memory to another and things will work exactly like you would expect (i.e. not blow up). This breaks if your class, or any class your class contains, has as a member that is a pointer or a reference or a class that has a virtual function. Essentially, if pointers have to be involved somewhere, its not Plain Old Data.


据我所知,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)

参考:

http://en.cppreference.com/w/cpp/types/is_pod http://en.wikipedia.org/wiki/Plain_old_data_structure http://en.wikipedia.org/wiki/Plain_Old_C++_Object 文件type_traits


POD(普通旧数据)对象具有这些数据类型中的一种——基本类型、指针、联合、结构、数组或类——没有构造函数。相反,非pod对象是存在构造函数的对象。POD对象在获得与其类型对应的适当大小的存储空间时开始其生命周期,在对象的存储空间被重用或释放时结束其生命周期。

PlainOldData类型也不能包含:

虚函数(它们自己的或继承的) 虚拟基类(直接或间接)。

PlainOldData更宽松的定义包括带有构造函数的对象;但不包括那些虚拟的东西。PlainOldData类型的重要问题是它们是非多态的。继承可以用POD类型完成,但是它应该只用于ImplementationInheritance(代码重用),而不是多态性/子类型。

一个常见的(虽然不是严格正确的)定义是PlainOldData类型是没有VeeTable的任何类型。


POD的概念和类型特征std::is_pod将在c++ 20中被弃用。有关进一步信息,请参阅此问题。


使用static_assert从c++ 11到c++ 17和POD效果的所有非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上有少量的测试,但它太少了。维护者:如果你读了这篇文章,请合并。我懒得在https://softwareengineering.stackexchange.com/questions/199708/is-there-a-compliance-test-for-c-compilers上查看所有提到的c++测试套件项目

#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和非POD呢?

c++最初是作为C的扩展而诞生的。虽然现代c++不再是严格意义上的C的超集,但人们仍然期望两者之间具有高度的兼容性。一个平台的“C ABI”还经常充当该平台上其他语言的事实上的标准中介语言ABI。

粗略地说,POD类型是一种与C兼容的类型,也许同样重要的是与某些ABI优化兼容。

为了与C兼容,我们需要满足两个约束条件。

布局必须与对应的C类型保持一致。 该类型必须以与相应的C类型相同的方式传递给函数并从函数返回。

某些c++特性与此不兼容。

虚方法要求编译器插入一个或多个指向虚方法表的指针,这在C语言中是不存在的。

用户定义的复制构造函数、移动构造函数、复制赋值和析构函数对参数传递和返回都有影响。许多C abi在寄存器中传递和返回小参数,但是传递给用户定义的构造函数/赋值/析构函数的引用只能处理内存位置。

因此,有必要定义哪些类型可以“兼容C”,哪些类型不能。c++ 03在这方面有点过于严格,任何用户定义的构造函数都将禁用内置构造函数,任何试图将它们添加回来的尝试都将导致它们是用户定义的,因此类型为非pod。c++ 11开放了很多东西,允许用户重新引入内置构造函数。