C++中的POD类型是什么?

What are POD types in C++?

我遇到过这个词pod类型好几次。这是什么意思?


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

A Plain Old Data Structure in C++ is an aggregate class that contains only PODS as members, has no user-defined destructor, no user-defined copy assignment operator, and no nonstatic members of pointer-to-member type.

在C++ 98/03的答案中可以找到更详细的内容。C++ 11改变了POD周围的规则,大大放松了它们,因此这里需要一个后续的答案。


非常非正式:

POD是一种类型(包括类),其中C++编译器保证在结构中不会有"魔法":例如,指向VTABLE的隐藏指针,当它被映射到其他类型(至少是目标的POD)时,应用到地址的偏移量,构造函数或析构函数。粗略地说,一个类型是一个pod,其中只有内置类型和它们的组合。结果是"表现得像"C类型。

不太非正式:

  • intcharwchar_tboolfloatdouble是豆荚,long/shortsigned/unsigned是豆荚。
  • 指针(包括指向函数的指针和指向成员的指针)是pods,
  • enums是豆荚
  • constvolatile荚是一个荚。
  • pods的classstructunion是一个pod,前提是所有非静态数据成员都是public,并且没有基类,也没有构造函数、析构函数或虚拟方法。静态成员不会阻止在这个规则下成为一个pod。这个规则在C++ 11中改变了,并且允许某些私有成员:一个具有所有私有成员的类可以是一个POD类吗?
  • 维基百科说pod不能有指向成员的指针类型的成员是错误的。或者更确切地说,它是正确的C++ 98的措辞,但TC1明确指出,指向成员是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)."


普通旧数据

简而言之,它是所有内置的数据类型(如intcharfloatlongunsigned chardouble等)和POD数据的所有聚集。是的,这是一个递归定义。;)

更清楚地说,pod就是我们所说的"结构":一个单元或一组只存储数据的单元。


据我所知,pod(plainolddata)只是一个原始数据-它不需要:

  • 待施工,
  • 被摧毁,
  • 要有自定义运算符。
  • 不能有虚拟功能,
  • 不能重写运算符。

如何检查某个东西是否是pod?好吧,有一个结构叫做std::is_pod

1
2
3
4
5
6
7
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)>
  { };
}

(来自标题类型u特性)


参考:

  • http://en.cppreference.com/w/cpp/types/is_pod
  • http://en.wikipedia.org/wiki/plain_old_data_结构
  • http://en.wikipedia.org/wiki/plain_old_c++_对象
  • 文件类型特征


pod(纯旧数据)对象具有以下数据类型之一:基本类型、指针、联合、结构、数组或类,没有构造函数。相反,非pod对象是一个构造函数存在的对象。当pod对象获得其类型大小合适的存储时,pod对象将开始其生存期;当对象的存储被重用或释放时,pod对象的生存期将结束。

PlainOldData类型也不能具有以下任何类型:

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

对plainolddata的更宽松的定义包括带有构造函数的对象;但不包括那些带有虚拟任何东西的对象。普通数据类型的一个重要问题是它们是非多态的。继承可以用pod类型来完成,但是它只能用于实现继承(代码重用),而不能用于多态性/子类型。

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


EDOCX1的1个非POD病例的例子:从C++ 11到C++ 17和POD效应

在C++ 11中添加了EDCOX1 0,所以现在让我们考虑这个标准。

Edcx1·0将从C++ 20中删除,如在HTTPS://StasOfFult.COM/A/443553/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++测试套件项目:HTTPS://StudioExchange .StCdExchange .COM/DISSMS/19708/IS-A兼容测试编译器-C编译器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#include <type_traits>
#include
#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上游。

测试:

1
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中被弃用。有关详细信息,请参阅此问题。


Why do we need to differentiate between POD's and non-POD's at all?

C++作为C的扩展开始了它的生命,而现代C++不再是C的严格超集,人们仍然期望两者之间具有高度的兼容性。

大致来说,pod类型是与c兼容的类型,可能同样重要的是与某些abi优化兼容。

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

  • 布局必须与相应的C类型相同。
  • 类型必须以与对应的C类型相同的方式传递到函数并从函数返回。
  • 某些C++特性与此不兼容。

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

    用户定义的复制构造函数、移动构造函数、复制分配和析构函数都会影响参数传递和返回。许多c abis在寄存器中传递并返回小参数,但是传递给用户定义的构造函数/分配/析构函数的引用只能处理内存位置。

    因此,需要定义哪些类型可以预期为"C兼容",哪些类型不能。C++ 03在这方面有些过于严格。C++ 11打开了不少东西。


    使用C++,普通的旧数据不仅仅意味着INT、Car等是唯一使用的类型。简单的旧数据实际上意味着,在实践中,您可以将结构memcpy从内存中的一个位置移到另一个位置,并且事情将完全按照预期工作(即,不会爆炸)。如果类或类包含的任何类作为指针或引用的成员或具有虚拟函数的类,则此操作将中断。从本质上讲,如果指针必须在某个地方涉及,它就不是简单的旧数据。