我遇到过几次POD-type这个术语。 这是什么意思?
当前回答
普通旧数据
简而言之,它是所有内置数据类型(例如int、char、float、long、unsigned char、double等)和POD数据的所有聚合。是的,这是递归定义。;)
更清楚地说,POD就是我们所说的“结构体”:仅存储数据的单元或单元组。
其他回答
为什么我们需要区分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开放了很多东西,允许用户重新引入内置构造函数。
POD(普通旧数据)对象具有这些数据类型中的一种——基本类型、指针、联合、结构、数组或类——没有构造函数。相反,非pod对象是存在构造函数的对象。POD对象在获得与其类型对应的适当大小的存储空间时开始其生命周期,在对象的存储空间被重用或释放时结束其生命周期。
PlainOldData类型也不能包含:
虚函数(它们自己的或继承的) 虚拟基类(直接或间接)。
PlainOldData更宽松的定义包括带有构造函数的对象;但不包括那些虚拟的东西。PlainOldData类型的重要问题是它们是非多态的。继承可以用POD类型完成,但是它应该只用于ImplementationInheritance(代码重用),而不是多态性/子类型。
一个常见的(虽然不是严格正确的)定义是PlainOldData类型是没有VeeTable的任何类型。
POD代表普通旧数据——也就是说,一个没有构造函数、析构函数和虚成员函数的类(无论是用关键字struct还是用关键字class定义的)。维基百科关于POD的文章更详细一些,并将其定义为:
c++中的普通旧数据结构是一个聚合类,它只包含PODS作为成员,没有用户定义的析构函数,没有用户定义的复制赋值操作符,也没有指针到成员类型的非静态成员。
更多的细节可以在c++ 98/03的答案中找到。c++ 11改变了围绕POD的规则,极大地放松了它们,因此需要在这里进行后续回答。
使用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的概念和类型特征std::is_pod将在c++ 20中被弃用。有关进一步信息,请参阅此问题。
推荐文章
- IEEE 754浮点数不能精确表示的第一个整数是哪一个?
- c++中size_t和int的区别是什么?
- 在C和c++中静态变量存储在哪里?
- 为什么标准迭代器范围是[begin, end]而不是[begin, end]?
- c++双地址操作符?(& &)
- 我看到VARCHAR(255)如此频繁地使用(而不是其他长度),有什么好的原因吗?
- 函数标题中的箭头操作符(->)
- 如何在c++中初始化一个向量
- 返回类型为'?:'(三元条件运算符)
- 当分配vector时,它们使用的是堆上的内存还是堆栈上的内存?
- 互斥实例/教程?
- 如何添加一个'或'条件在#ifdef
- 纯虚函数的实现
- 如何比较Python中对象的类型?
- 为什么在c++中声明enum时使用typedef ?