C++中的POD类型有哪些?

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

406349 次浏览

POD代表纯旧数据-也就是说,一个类(无论使用关键字struct还是关键字class定义)没有构造函数、析构函数和虚拟成员函数。

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

C++11改变了围绕POD的规则,大大放松了它们,因此需要一个后续的答案

纯旧数据

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

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

非常非正式地:

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

非正式的:

  • intcharwchar_tboolfloatdouble是POD,它们的long/shortsigned/unsigned版本也是如此。
  • 指针(包括指向函数的指针和指向成员的指针)是POD,
  • enums是POD
  • constvolatile POD是POD。
  • classstructunion的POD是POD,前提是所有非静态数据成员都是public,并且它没有基类,也没有构造函数、析构函数或虚方法。静态成员不会阻止此规则下的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-struct是一个聚合类,它没有非POD-struct、非POD-Union(或此类类型的数组)类型的非静态数据成员或引用,也没有用户定义的复制运算符和用户定义的析构函数。类似地,POD-Union是一个聚合联合,它没有非POD-struct、非POD-Union(或此类类型的数组)类型的非静态数据成员或引用,也没有用户定义的复制运算符和用户定义的析构函数。

8.5.1(1):“聚合是一个数组或类(第9条),没有用户声明的构造函数(12.1),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),也没有虚函数(10.3)。”

对于C++,普通旧数据不仅仅意味着int、char等是唯一使用的类型。普通旧数据实际上意味着你可以将一个struct memcpy从内存中的一个位置转移到另一个位置,事情会像你预期的那样工作(即不会爆炸)。如果你的类或你的类包含的任何类作为指针或引用的成员,或者一个具有虚拟函数的类,这就打破了这一点。本质上,如果必须在某个地方涉及指针,那就不是普通旧数据。

据我所知,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(普通旧数据)对象具有这些数据类型之一(基本类型、指针、联合、结构、数组或类),没有构造函数。相反,非POD对象是存在构造函数的对象。POD对象在获得与其类型大小合适的存储时开始其生命周期,在对象的存储被重用或释放时结束其生命周期。

PlainOldData类型也不能有任何:

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

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

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

POD和type traitstd::is_pod的概念将在C++20中被弃用。

所有非POD病例的例子static_assert从C++11到C++17和POD效应

std::is_pod是在C++11中添加的,所以现在让我们考虑一下这个标准。

std::is_pod将从C++20中删除,如https://stackoverflow.com/a/48435532/895245所述,让我们在替换的支持到达时更新它。

随着标准的发展,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++测试套件项目。

#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 >= 201103Lstatic_assert(std::is_pod<C>(), "");#elsestatic_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-podstatic_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兼容,我们需要满足两个约束。

  1. 布局必须与相应的C类型相同。
  2. 该类型必须以与相应的C类型相同的方式传递给函数和从函数返回。

某些C++功能与此不兼容。

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

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

C++03在这方面有点过于严格,任何用户定义的构造函数都会禁用内置构造函数,任何试图将它们添加回来的尝试都会导致它们是用户定义的,因此类型是非pod。