84

通俗的讲,琐碎类型、标准布局类型和 POD 有什么区别?

具体来说,我想确定是否new T不同于new T()任何模板参数T。我应该选择哪种类型is_trivial的特征?is_standard_layoutis_pod

(作为一个附带问题,这些类型特征中的任何一个都可以在没有编译器魔法的情况下实现吗?)

4

3 回答 3

77

我不认为它可以用真正外行的术语来完成,至少没有很多额外的解释。重要的一点是静态与动态初始化,但向外行解释这本身就是几页......

POD 在 C++98 中(错误)定义。实际上涉及两个不同的意图,都没有很好地表达:1)如果你在 C++ 中编译一个 C 结构声明,你得到的应该等同于你在 C 中得到的。2)一个 POD 将只需要/使用静态(非动态)初始化。

C++0x/11 (几乎)完全放弃了“POD”名称,转而采用“琐碎”和“标准布局”。标准布局旨在捕获第一个意图——创建与您在 C 中获得的布局相同的东西。琐碎旨在捕获对静态初始化的支持。

由于new Tvs.new T()处理初始化,您可能需要is_trivial.

我不确定是否需要编译器魔法。我的直接反应可能是肯定的,但是知道人们对 TMP 所做的一些事情,我很难确定有人不能这样做......

编辑:例如,也许最好只引用 N3290 中的示例:

struct N { // neither trivial nor standard-layout
   int i;
   int j;
    virtual ~N();
};

struct T { // trivial but not standard-layout
    int i;
private:
    int j;
};

struct SL { // standard-layout but not trivial
    int i;
    int j;
    ~SL();
};

struct POD { // both trivial and standard-layout
    int i;
    int j;
};

你可以毫无疑问地猜到,POD它也是一个 POD 结构。

于 2011-06-27T17:39:04.367 回答
8

对于 POD 类型new T()是值初始化(将对所有成员进行值初始化),并且new T不会初始化成员(默认初始化)。有关不同初始化形式之间的差异,请参阅此问题。底线:你需要is_pod.

于 2011-06-27T17:28:57.377 回答
4

布局是类、结构或联合对象的成员在内存中的排列方式。这可能是连续的,也可能不是。语言通常指定布局,但如果有虚函数、虚拟基类等,那么编译器可以自由选择布局,这可能不是连续的。这导致我们无法适当地序列化对象或传递给用其他语言(如 C)或 memcopy 等函数编写的程序的几个问题,因为我们无法可靠地复制数据,因为它不在连续的位置。

为了使编译器和 c++ 程序能够支持上述操作,c++ 为简单的结构和类引入了 3 个类别。

琐碎的

如果遵循规则,则类或结构是微不足道的:

  • 没有虚函数或虚基类
  • 没有用户定义的构造函数/操作符/析构函数
  • 基类应该是微不足道的
  • 所有的班级成员都应该是微不足道的

如果一个类是微不足道的,那么它的布局是连续的,但可能会有相应的填充,编译器可以自由选择布局中成员的顺序。因此,即使我们可以 memcopy 对象,如果我们将该对象复制到 C 程序中也是不可靠的。我们可以在同一个类本身中有不同的访问说明符,如果使用参数化构造函数,显然我们必须指定默认构造函数。但是如果你想让类保持简单,那么你应该明确地将构造函数设为默认值。构造函数应该是公共的。

标准布局

  • 没有虚函数和虚基类
  • 所有非静态成员都应具有相同的访问说明符
  • 所有非静态成员都应该是标准布局
  • 所有基类都应该是标准布局
  • 基类的所有成员都应该是静态的
  • 基类的类型和类的第一个非静态成员不能相同

标准布局定义明确,可以可靠地进行内存复制并适当地传递给 C 程序。标准布局函数也可以具有用户定义的特殊成员函数,如构造函数和析构函数。

POD(普通旧数据)

如果一个类或结构既是普通布局又是标准布局,则称其为 POD。每个成员都按照声明对象时指定的顺序存储。POD 类应该具有 POD 非静态数据成员。POD 类可以可靠地复制或传递给 C 程序。

C++ 程序的类是平凡的、标准的布局,因此是 POD。

#include<iostream>
#include<type_traits>
class xyz
{
public:
    int a;
    int b;
    xyz() = default;
    xyz(int x, int y) :a(x), b(y) {}
};
int main() {
    std::cout << std::is_trivial<xyz>() << std::endl;//true
    std::cout << std::is_standard_layout<xyz>() << std::endl;//true
    std::cout << std::is_pod<xyz>() << std::endl;//true
}

文字类型

对于文字类型,布局可以在编译时确定。文字类型的示例是 void、标量类型(如 int、float 等)、引用、void 数组、标量类型或引用以及具有普通析构函数的类,以及一个或多个不是移动或复制构造函数的 constexpr 构造函数。此外,它的所有非静态数据成员和基类都必须是文字类型并且不是易失性的

于 2020-09-23T05:56:52.933 回答