什么是trivial/POD类型?

C++20标准之前,POD类型指符合C的平凡旧数据结构(Plain Old Data),即类似C中结构体的平凡的、不具备特殊操作的数据结构,可以用于元数据交换的数据类型,直接以二进制和C库兼容的数据类型。
设立此概念的初衷是为了描述那些 和 C 中结构体的概念相似的类型
但是,这个概念是太过于抽象和难以精确、严禁描述的。通过标准中对 POD 定义的变动,甚至在 C++20 中 std::is_pod 被弃用等种种变化可以看出,这是一个很难刻画的概念。

C++20标准后规则上,POD类型拆分为以下两个定义

C++20标准将POD类型的概念拆分为两个基本概念的合集,即平凡的(trivial)和标准布局(standard layout)。
C++20标准之前,有std::is_pod可以判对象是否是POD类型, 但在C++20之后std::is_pod被弃用,建议使用两个新的判断条件std::is_trivial && std::is_standard_layout去代替。

1)平凡的

一个平凡的类或者结构体应包含以下定义

  • 有平凡的缺省构造函数,可用这样的默认语法:(SomeConstructor() = default;)

  • 有平凡的copy与move构造函数,可用默认语法.

  • 有平凡的copy与move运算符,可用默认语法.

  • 有平凡的destructor,不能是虚函数.

  • 不包含虚函数和虚基类

2)标准布局的

  • 所有非静态成员有相同的访问权限(public protected privete)

  • 派生类中有非静态成员,且只有一个仅包含静态成员的基类。

  • 基类有非静态成员,派生类中没有非静态成员

  • 类中的第一个非静态成员的类型与其基类不同

  • 没有虚函数与虚基类

  • 所有非静态数据成员均符合标准布局类型,基类也符合标准布局

POD类型的体现

POD类型可以直接使用memcpy和memset来操作,而不损失功能

POD 只是可以安全使用 memcpy 的充分非必要条件。其实只要这个类型是 TriviallyCopyable 的,那就能安全地使用 memcpy 去拷贝它。而 POD 是相比 TriviallyCopyable 更加严格的限制。

下面我们看看为什么会有POD类型的概念

首先,众所周知,C++ 的类里头,有六个最为特殊的成员函数:

  • 默认构造函数,即 T::T( )
  • 拷贝构造函数,即 T::T( (const) (volatile) T&)
  • 拷贝赋值运算符,即 T::operator=( (const) (volatile) T&)
  • 析构函数,即 T::~T()移动构造函数,即 T::T( (const) (volatile) T&&)
  • 移动赋值运算符,即 T::operator=( (const) (volatile) T&&)
    不严谨地来说,只要这个类的以上对应的成员函数,不做什么”额外“的动作,那么这个成员函数就是 Trivial (平凡) 的。举一些例子吧。
struct Foo
{int x;
};

Foo 六个成员函数全部都是平凡的,因为:默认构造函数不做任何初始化动作(连 .x 初始化为 0 也不会做)拷贝/移动构造函数只是老老实实地依次拷贝/移动各个成员拷贝/移动赋值函数只是老老实实地依次拷贝/移动赋值各个成员析构函数什么也没做.

struct Foo
{int x;Foo() = default;Foo(const Foo &) = default;Foo(Foo &&) = default;Foo& operator=(const Foo &) = default;Foo& operator=(Foo &&) = default;~Foo() = default;
};

同样,2的六个成员函数全部是trivial的

struct Foo
{int x;Foo() {}Foo(const Foo & src) : x(src.x) {}Foo(Foo && src) : x(std::move(src.x)) {}Foo& operator=(const Foo &) {}Foo& operator=(Foo &&) {}~Foo() {}
};

抱歉,这里的六个构造函数全都不是trivial的,因为哪怕就是空的函数体,或者是做了和默认构造一样的操作,也是做了特殊操作,那么我们默认这改变了默认行为,所以是非trivial的。

struct Goo
{Goo() = default;Goo(const Goo&) { std::cout << 2333 << std::endl;} // 我不平凡!Goo(Goo &&) = default;Goo& operator=(const Goo&) = default;Goo& operator=(Goo &&) = default;
};struct Foo : Goo
{Foo() = default;Foo(const Foo &) = default;Foo(Foo &&) = default;Foo& operator=(const Foo &) = default;Foo& operator=(Foo &&) = default;~Foo() = default;
};

同样,4的拷贝构造函数也是非trivial,Foo的拷贝构造也是非trival,因为Foo一定会调用Goo的非trivial拷贝构造函数

struct Goo
{Goo() = default;Goo(const Goo&) = default;Goo(Goo &&) = default;Goo& operator=(const Goo&) = default;Goo& operator=(Goo &&) = default;virtual void f() {}
};struct Foo : Goo
{Foo() = default;Foo(const Foo &) = default;Foo(Foo &&) = default;Foo& operator=(const Foo &) = default;Foo& operator=(Foo &&) = default;~Foo() = default;
};

由于有虚函数,除了析构函数外,其他五个ctor都会对虚指针做一些额外的工作,所以也不满足trivial的概念。
到此为止,差不多就能够理解什么是trivial 平凡的类了

条件比较繁琐,可以用以下函数来做检测:

#include <type_traits>
#include <iostream>int main()
{using namespace std;cout << is_trivially_default_constructible<Foo>::value << std::endl;cout << is_trivially_copy_constructible<Foo>::value << std::endl;cout << is_trivially_move_constructible<Foo>::value << std::endl;cout << is_trivially_copy_assignable<Foo>::value << std::endl;cout << is_trivially_move_assignable<Foo>::value << std::endl;cout << is_trivially_destructible<Foo>::value << std::endl;
}

追根溯源

一般而言,在C++库的底层,一个对象的生命周期都会经历以下几个步骤:

#include <memory>template <typename T>
void life_of_an_object
{std::allocator<T> alloc;// 1. 通过 allocator 抑或是 malloc 等其他方式分配出空间T * p = alloc.allocate(1);// 2. 通过 placement new,(在需要的时候) 动态地构造对象new (p) T(); // 这里是默认构造,也可能是带参数的构造方式如 new (p) T(构造参数...);// 3. 通过显式调用析构函数,(在需要的时候) 动态地销毁对象p->~T();// 4. 通过分配函数的对应的解分配手段,解分配空间alloc.deallocate(p, 1);
}

如果 T 类型是平凡默认构造的,则意味着步骤 2 其实是不需要的——反正 T 类型在默认构造的时候,什么也没做,没有清零内部空间什么的。步骤 2 不做不会对程序的正确性构成任何影响。

如果 T 类型是平凡析构的,则意味着步骤 3 其实是不需要的——反正 T 类型在析构的时候,什么也没做,不用释放内存,不用 close 文件,不用释放 socket…… 。步骤 3 不做同样也不会对程序的正确性构成任何影响。

那我们在模板库中,就可以为对应的成员函数为 Trivial 的类型,做单独的特化,从而提高性能。诚然,对于这种最简单、最显而易见的情况,哪个编译器做不了优化,哪个是辣鸡。

但是情况要是复杂一些呢?如果被析构的是一段区间呢?

struct Foo
{~Foo() = default;
};#include <list>
#include <set>template <typename Iterator>
void myDestroy(Iterator first, Iterator last)
{using value_type = typename std::iterator_traits<Iterator>::value_type;while (first != last) {first->~value_type();++first;}
}template void myDestroy(Foo*, Foo*);
template void myDestroy(std::set<Foo>::iterator, std::set<Foo>::iterator);
template void myDestroy(std::list<Foo>::iterator, std::list<Foo>::iterator);
image.png

好家伙,就嗯在链表,二叉树上面便利了一遍,这样的遍历,其实是毫无用处毫无意义的吧,对吧?只是遍历了一遍,不做任何事。


http://www.taodudu.cc/news/show-4067500.html

相关文章:

  • trivial destructor
  • GCC9.4 memset() clearing an object of type with no trivial copy-assignment [-Werror=class-memaccess]
  • C++:Trivial、Standard-Layout 和 POD
  • [C++] 中的trivial destructor
  • C/C++编程:trivial和non-trivial
  • trivial/nontrival函数
  • C++高阶 什么是Trivial types?
  • c++中的 trivial destructor
  • C++ trivial和non-trivial构造函数及POD类型
  • c++中的trivial
  • C++ — POD类型以及trivial 和 non-trivial
  • 时间管理四象限
  • python 四象限图_方法:高效处理工作的四象限图
  • 四象限法推导lm曲线_四象限法分析
  • c语言四象限线性差值算法
  • Tebleau-四象限图
  • Gartner 魔力四象限 -- 应用安全检测
  • 无刷直流电机四象限matlab pudn,一种无刷直流电机四象限运行的PWM控制方法与流程...
  • 四象限法推导lm曲线_提出研究问题的“四象限法”
  • Gartner 魔力四象限
  • 电机四象限运行
  • [项目管理-28]:四象限法与任务的时间优先级管理
  • python 四象限图_「四象限图」excel制作四象限图的方法图解步骤 - seo实验室
  • python 四象限图_如何快速绘制出四象限图?
  • python 四象限图_Tableau技巧|制作四象限图
  • 四象限运行模式_四象限变频器原理及系统构成
  • 四象限时间管理有多好用?
  • 3Dtouch开发内容
  • iPhone13有3D Touch吗 3D Touch有什么用
  • 3d touch android,苹果3DTouch好用?安卓这个功能不比它差!

[c/c++]trivial/POD类型和standard layout相关推荐

  1. 数据库中的字段varchar类型和char类型的区别?

    数据库中的字段varchar类型和char类型的区别? 目录 数据库中的字段varchar类型和char类型的区别?

  2. DATETIME类型和BIGINT 类型互相转换

    项目中使用BIGINT来存放时间,以下代码用来转换时间类型和BIGINT类型 SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ========= ...

  3. Java中基本数据类型和Object之间的关系

    1.基本数据类型和Object之间是没有关系的 2.基本数据类型的包装类 例如int 的包装类Integer的父类则是Object 输入:12 解释:在赋值的过程中进行自动装箱

  4. java long 对应mybati类型_修改 mybatis-generator 中数据库类型和 Java 类型的映射关系...

    使用 mybatis-generator 发现数据库类型是 tinyint(4) , 生成 model 时字段类型是 Byte ,使用的时候有点不便 数据库的类型和 Model 中 Java 类型的关 ...

  5. linux 进程间通信 dbus-glib【实例】详解三 数据类型和dteeth(类型签名type域)(层级结构:服务Service --> Node(对象、object) 等 )(附代码)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

  6. MySQL调优(二):数据类型和schema优化,MySQL8.0取消查询缓存的原因

    数据类型和schema优化 数据类型的优化 合理使用范式和反范式 三大范式: 1.表不可分 2.不能存在传递依赖 3.表里其他列的值必须唯一依赖于主键 约定大于规范,没有必要严格遵守范式,以业务为准, ...

  7. java 8 Stream中操作类型和peek的使用

    文章目录 简介 中间操作和终止操作 peek 结论 java 8 Stream中操作类型和peek的使用 简介 java 8 stream作为流式操作有两种操作类型,中间操作和终止操作.这两种有什么区 ...

  8. MySQL数据类型和Java数据类型对应关系表

    MySql 数据类型和 Java 数据类型之间的转换是很灵活的. 一般来讲,任何 MySql 数据类型都可以被转换为一个 java.lang.String,任何 MySql 数字类型都可以被转换为任何 ...

  9. char类型和Unicode编码

    [0]README 0.1)本文对 char类型和Unicode编码 的总结并不完整,仅供参考: 0.2)本文获取Unicode辅助字符的代码点的idea转自:  http://blog.csdn.n ...

  10. php中自动转换、强制转换、其他数据类型和bool转换

    0x01 自动转换 运算过程需要的数据类型和提供的数据类型不一致,将数据类型转为自己需要的类型 <?phpheader('content-type:text/html;charset=utf-8 ...

最新文章

  1. Android之传感器(一)
  2. Redis进阶-细说分布式锁
  3. hplaserjet1022老提示打印错误,hp laserjet 1022 在win7上正确的安装步骤
  4. 兼容门:先卸载腾讯QQ,再卸载360软件!
  5. Eclipse Console 加大显示的行数,禁止弹出
  6. android 中自定义键盘,【图片】自定义属于自己的专属键盘的思路!!!(需要有android编程基础)【exagear吧】_百度贴吧...
  7. 备份事务日志时遇到 log corruption
  8. android 调试好事工具类,Android 工具类之总结 Hua
  9. 杭电1862EXCEL排序
  10. nodejs模块demo: request-promise-native tough-cookie
  11. 原生JS实现Ajax请求
  12. 码率多少算是正常的1080p_MP3的频率、比特率、码率与音质的关系(网络整理)...
  13. 人人商城小程序 用户登录授权接口 wx.getUserProfile后,个别用户出现无法登录的问题
  14. CST材料库相关问题
  15. python list 对时间排序小结。
  16. vue如何关闭eslint语法检查
  17. 【HTTP】HTPP学习笔记
  18. 几大原型开发软件对比[转]
  19. iVX案例制作(1)—图片查看器
  20. Linux系统自动化安装(二)

热门文章

  1. IndexedDB 包装库 idb
  2. 边境的悍匪—机器学习实战:第十六章使用RNN和注意力机制进行自然语言处理
  3. b站谈服务器崩溃后其他站点,B站服务器崩溃后,蒙古上单和陈睿一起上了热搜...
  4. python autoit3自动化测试_autoit-解决非标准B/S自动化测试的一个很好的思路
  5. 拒做背锅侠!如何利用网站性能优化驱动产品体验提升
  6. [转载]Oraclenbsp;grantnbsp;revokenbsp;…
  7. 最全的关于硬件测试的解读
  8. 爬虫链家7万条数据,告诉你二手房市场的现状
  9. L 2 聚焦和发散思维模式
  10. html+css 炫彩流光按钮