C++之std::is_trivially_copyable(平凡可复制类型检测)
目录
1.C++基础回顾
1.1.平凡类型
1.2.平凡可复制类型
1.3.标准布局类型
2.std::is_trivially_copyable
2.1.定义
2.2.使用
2.3.总结
1.C++基础回顾
在C++11中,平凡类型(Trivial Type)、平凡可复制类型(TrivialCopyable)、标准布局类型(Standard-layout Type)是描述类在内存中布局特性的术语,它们与类的构造、拷贝、赋值和销毁行为有关,也影响着类的内存布局和对齐方式。下面用通俗的语言解释这些概念:
1.1.平凡类型
指那些在内存中的行为非常简单的类。它们的构造函数、析构函数、拷贝构造函数和赋值运算符都没有自定义实现,完全由编译器提供的默认行为即可,而且也不能包含虚函数以及是虚基类的父类, 这意味着这些类的对象可以像基本数据类型一样被创建和销毁,不需要特殊的资源管理代码。
以下是平凡类型和非平凡类型的示例代码展示,参考代码如下:
#include <iostream>// 平凡类型:没有任何自定义的构造函数、析构函数、拷贝控制成员
struct TrivialType {int a;double b;
};// 非平凡类型:至少有一个自定义的特殊成员函数
struct NonTrivialType1 {int a;double b;// 自定义构造函数NonTrivialType1() : a(0), b(0.0) {}// 自定义拷贝赋值运算符NonTrivialType1& operator=(const NonTrivialType1& other) {a = other.a;b = other.b;return *this;}// 自定义析构函数~NonTrivialType1() {std::cout << "NonTrivialType1 destroyed\n";}
};//使用=default关键字可以显式地声明默认的构造函数,从而使得类型恢复 “平凡化”。
struct TrivialType2 {int a;double b;// 自定义构造函数TrivialType2() : a(0), b(0.0) {}TrivialType2() = default;
};int main() {TrivialType t1, t2;t2 = t1; // 平凡类型的赋值操作是平凡的NonTrivialType nt1, nt2;nt2 = nt1; // 非平凡类型的赋值操作不是平凡的std::cout << "TrivialType is trivially:" << std::is_trivially<TrivialType>::value << std::endl; //输出:truestd::cout << "NonTrivialType1 is trivially:" << std::is_trivially<NonTrivialType1>::value << std::endl; //输出:falsestd::cout << "TrivialType2is trivially:" << std::is_trivially<TrivialType2>::value << std::endl; //输出: falsereturn 0;
}
在这个示例中:
TrivialType 是一个平凡类型,因为它没有任何自定义的特殊成员函数。它的构造、拷贝、移动、赋值和析构操作都是由编译器提供的默认实现。
NonTrivialType1 是一个非平凡类型,因为它至少有一个自定义的特殊成员函数(在这个例子中是构造函数、拷贝赋值运算符和析构函数)。这意味着它至少有一个操作不能由编译器提供的默认实现来完成。
TrivialType2虽然重新定义了构造函数,但是使用=default,使用=default关键字可以显式地声明默认的构造函数,从而使得类型恢复 “平凡化”。
注意事项:
即使类没有显示定义特殊成员函数,如果类中有虚函数或虚基类,它也不是平凡类型。
类中如果有动态内存分配(如指针成员)或需要特殊资源管理的成员,也不是平凡类型。
平凡类型的所有特殊成员函数都是平凡的,这意味着它们可以没有函数体(即使用编译器提供的默认实现)。
平凡类型在C++中很重要,因为它们可以提高效率,允许编译器进行更多的优化。例如,平凡类型的拷贝和赋值可以通过简单的内存复制完成,而不需要调用任何成员函数。
1.2.平凡可复制类型
是平凡类型的一个扩展,它不仅包括所有平凡类型,还包括那些可以安全地被复制和移动的类型,即使这些类型不是平凡类型。例如,一个类可能有一个自定义的构造函数,但如果它保证对象的内容可以通过简单的位拷贝(bitwise copy)来复制,那么它也可以被认为是平凡可复制的。它必须满足两个条件:
- 类型可以被复制或移动,且不需要特殊的资源管理。
- 类型的所有特殊成员函数(构造函数、拷贝构造函数、移动构造函数、赋值运算符、移动赋值运算符和析构函数)都是平凡的或者被删除的(deleted)。
下列类型统称为可平凡复制类型:
- 标量类型
- 可平凡复制类类型
- 上述类型的数组
- 这些类型的有 cv 限定版本
说明:
一般来说,对于任何可平凡复制类型
T及T对象obj1,能复制obj1的底层字节到 char 或 unsigned char 或 std::byte (C++17 起) 的数组中,或到T的另一不同对象obj2中。obj1与obj2均不可为潜在重叠的子对象。如果复制
obj1的底层字节到这种数组中,然后复制结果内容回obj1中,那么obj1将保有其原值。如果复制obj1的底层字节到obj2中,那么obj2将保有obj1的值。底层字节能由 std::memcpy 或 std::memmove 复制,只要不访问存活的 volatile 对象即可。
具体示例我们将在后面给出。
1.3.标准布局类型
指那些在内存布局上满足一定规则的类。这些规则包括所有非静态成员的访问权限必须相同,类不能有虚函数或虚基类,且所有基类也必须是标准布局类型。标准布局类型的一个重要特性是它们的内存布局在不同的编译器和平台上是一致的,这对于跨平台的二进制数据交换非常重要,它必须满足以下条件:
1)类型的所有非静态数据成员都是公共的(public)。
2)类型不包含虚函数、虚基类或非标准布局的基类。
3)类型的所有基类都是标准布局类型。
4)类型不包含动态内存分配,如没有指向其自身类型的指针成员。
5)类型的所有数据成员的访问权限(public、protected、private)都是相同的。
示例代码如下:
#include <iostream>
#include <type_traits> // For std::is_standard_layout// 标准布局类型:没有任何虚函数或虚基类,所有数据成员都是公共的
struct StandardLayoutType {int a;double b;
};// 非标准布局类型:包含虚函数
struct NonStandardLayoutTypeWithVirtualFunction {virtual void dummy() {}int a;double b;
};// 非标准布局类型:包含非标准布局基类
struct NonStandardBase {int a;
protected:double b; // Data member with non-public access
};struct NonStandardLayoutTypeWithNonStandardBase : NonStandardBase {int c;
};// 标准布局类型:尽管有继承,但基类是非虚继承且本身也是标准布局
struct StandardLayoutTypeWithInheritance : StandardLayoutType {char c;
};int main() {std::cout << std::boolalpha; // Print bool values as true/false// 检查是否为标准布局类型std::cout << "Is StandardLayoutType standard layout? " << std::is_standard_layout<StandardLayoutType>::value << std::endl;std::cout << "Is NonStandardLayoutTypeWithVirtualFunction standard layout? " << std::is_standard_layout<NonStandardLayoutTypeWithVirtualFunction>::value << std::endl;std::cout << "Is NonStandardLayoutTypeWithNonStandardBase standard layout? " << std::is_standard_layout<NonStandardLayoutTypeWithNonStandardBase>::value << std::endl;std::cout << "Is StandardLayoutTypeWithInheritance standard layout? " << std::is_standard_layout<StandardLayoutTypeWithInheritance>::value << std::endl;return 0;
}
在这个示例中:
a) StandardLayoutType 是一个标准布局类型,因为它没有任何虚函数或虚基类,所有数据成员都是公共的。
b) NonStandardLayoutTypeWithVirtualFunction 不是标准布局类型,因为它包含一个虚函数。
c)NonStandardLayoutTypeWithNonStandardBase 不是标准布局类型,因为它有一个基类 NonStandardBase,该基类包含受保护的成员,不符合所有数据成员都是公共的规则。
d)StandardLayoutTypeWithInheritance 是一个标准布局类型,尽管它继承StandardLayoutType,但继承是不带虚函数的,且所有数据成员都是公共的。
2.std::is_trivially_copyable
2.1.定义
它是在标头 <type_traits> 定义
template< class T >
struct is_trivially_copyable;
主要用来判断T是否平凡可复制类型。
并非非潜在重叠子对象的可平凡复制类型的对象,是仅有的能以 std::memcpy 安全复制或以 std::ofstream::write() / std::ifstream::read() 序列化自/到二进制文件的 C++ 对象。
2.2.使用
示例1:
#include <type_traits>struct A { int m; };
static_assert(std::is_trivially_copyable_v<A> == true);struct B { B(B const&) {} };
static_assert(std::is_trivially_copyable_v<B> == false);struct C { virtual void foo(); };
static_assert(std::is_trivially_copyable_v<C> == false);struct D
{int m;D(D const&) = default; // -> 可平凡复制D(int x) : m(x + 1) {}
};
static_assert(std::is_trivially_copyable_v<D> == true);int main() {}
在这个示例中:
1) A是一个平凡可复制类型,因为它没有自定义的特殊成员函数,且可以被简单地复制和移动。
2) B有一个自定义的拷贝构造函数,所以它不是平凡可复制的。尽管它的赋值操作可能是平凡的,但拷贝构造函数的存在使得整个类型不是平凡可复制的。
3) C有一个虚函数,这使得它即使没有自定义的特殊成员函数,也不是平凡可复制的。虚函数的存在意味着类型需要有虚函数表(vtable),这违反了平凡可复制类型的定义。
4) D虽然有一个自定义的拷贝构造函数,但是有一个使用=default的构造函数,所以它也是平凡可复制的。
平凡可复制类型在C++中很重要,因为它们可以被编译器优化为没有额外开销的位拷贝操作,这对于性能敏感的程序是非常有益的。
示例2:
#include <iostream>
using namespace std;// trivially copyable
class A
{~A() = default; // trivially copyableA() {} // trivially copyableA(const A &) = default; // trivially copyableA(A &&) = default; // trivially copyableA &operator=(const A &) = default; // trivially copyableA &operator=(A &&) = default; // trivially copyable
};class B
{// 只要有任意自定义的下列行为即会变成 not trivially copyablevirtual void foo() = 0; // not trivially copyable// ~B() = delete; // not trivially copyable// ~B() {} // not trivially copyable// B(const B &) {} // not trivially copyable// B(B &&) {} // not trivially copyable// B &operator=(const B &) {} // not trivially copyable// B &operator=(B &&) {} // not trivially copyable
};// not trivially copyable
class C : public B
{
};// trivially copyable
class D
{
public:explicit D(int val) : d(val) {}int d;
};void TriviallyCopyableTest()
{cout << std::is_trivially_copyable<bool>::value << endl; // trivially copyablecout << std::is_trivially_copyable<char>::value << endl; // trivially copyablecout << std::is_trivially_copyable<int>::value << endl; // trivially copyablecout << std::is_trivially_copyable<float>::value << endl; // trivially copyablecout << std::is_trivially_copyable<double>::value << endl; // trivially copyablecout << std::is_trivially_copyable<std::nullptr_t>::value << endl; // trivially copyablecout << std::is_trivially_copyable<int *>::value << endl; // trivially copyablecout << std::is_trivially_copyable<A>::value << endl; // trivially copyablecout << std::is_trivially_copyable<A *>::value << endl; // trivially copyablecout << std::is_trivially_copyable<B>::value << endl; // not trivially copyablecout << std::is_trivially_copyable<B *>::value << endl; // trivially copyablecout << std::is_trivially_copyable<C>::value << endl; // not trivially copyablecout << std::is_trivially_copyable<string>::value << endl; // not trivially copyable
}
分析方法同上,我们在这里就不赘述了。
2.3.总结
在 C++11 及其之后的版本中,如果一个类型是可平凡复制的,那么你可以安全地通过 memcpy 或 memmove 等函数进行复制,而不需要担心可能的副作用(如析构函数的调用或虚函数的重新定向等)。然而,你应该注意,即使一个类型是可平凡复制的,也并不意味着你应该总是使用 memcpy 来进行复制;在许多情况下,使用赋值操作符或复制构造函数是更安全、更清晰的选择。
推荐阅读
可平凡复制类型
std::is_trivially_copyable
C++之std::is_pod(平凡的数据)
相关文章:
C++之std::is_trivially_copyable(平凡可复制类型检测)
目录 1.C基础回顾 1.1.平凡类型 1.2.平凡可复制类型 1.3.标准布局类型 2.std::is_trivially_copyable 2.1.定义 2.2.使用 2.3.总结 1.C基础回顾 在C11中,平凡类型(Trivial Type)、平凡可复制类型(TrivialCopyable&#x…...
宝石收集,tarjan
0宝石收集 - 蓝桥云课 (lanqiao.cn) nint(input()) s0input() mint(input()) mp[[] for i in range(n1)] for i in range(m):a,bmap(int,input().split())a1b1mp[a].append(b)import sys sys.setrecursionlimit(100000000) dfn[0 for i in range(n1)] low[0 for i in range(n1…...
python 面对对象 类 继承
继承 继承就是为了解决两个有大量重复性代码的类,抽象出一个更抽象的类放公共代码,主要是代码复用,方便代码的管理与修改 类的继承包括属性和方法,私有属性也可继承 class Person(): # 默认是继承object超类pass…...
Rust腐蚀怎么用服务器一键开服联机教程
1、进入控制面板 首次登陆需要点击下方重置密码,如何再点击登录面板,点击后会跳转到登录页面,输入用户名和密码登录即可 2、设置游戏端口 由于腐蚀的设置需要三个端口,它们用于游戏端口(必须为首选端口)&a…...
公共代理IP和独享代理IP之间的区别?
公共代理IP和独享代理IP在网络应用中扮演着不同的角色,它们之间的区别主要体现在使用方式、性能、安全性以及隐私保护等方面。以下是对这两种代理IP的详细对比和分析。 第一点就是使用的方式以及成本上的不同,公共代理IP,顾名思义࿰…...
基于Vue的前端自定义询问弹框与输入弹框组件的设计与实践
基于Vue的前端自定义询问弹框与输入弹框组件的设计与实践 摘要 随着技术的不断进步,前端开发面临越来越多的挑战,其中之一就是如何有效管理复杂的业务逻辑和用户体验。传统的整块应用开发方式在面对频繁的功能变更和用户体验优化时,往往显得…...
淘宝订单系统ERP中如何接入平台订单信息?(订单API)
淘宝开放平台中有交易API,里面有各种关于交易的API接口。但是申报应用权限的审核流程严格又漫长。不少公司费时费力的申请后,结果还是没有审批下来。 调用淘宝自定义接口custom,可以实现淘宝开放平台API的调用。技术人员会根据您需要的接口做…...
在Spring Boot项目中集成和使用MQTT
在物联网(IoT)应用中,MQTT(消息队列遥测传输)协议因其轻量级和高效性被广泛使用。在Spring Boot项目中,我们可以通过集成org.springframework.integration:spring-integration-mqtt依赖来实现对MQTT的支持。…...
14、设计模式之访问者模式
访问者模式 在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元…...
Excel如何换行不换格
在换行的字之间 按住Alt 回车...
Elasticsearch 8.1官网文档梳理 - 十五、Aggregations(聚合)
Aggregations ES 的聚合可以总结为三类:指标聚合、统计聚合、其他分析聚合。 Metric aggregations: 计算 field 的指标值,例如平均值、最大值、和等指标Bucket aggregations: 基于 field 的值、范围、或其他标准对 doc 分类&…...
计算机系统概论
目录 1. 计算机的分类 2. 计算机的发展简史 3. 计算机的硬件 1. 处理器(CPU) 2. 内存(Memory) 3. 存储设备 4. 输入输出设备 4. 计算机的软件 1. 软件的分类 1.1 系统软件 1.2 应用软件 2. 软件的特点 3. 软件开发 4…...
【Vue】diff 算法
diff的时机 当组件创建时,以及依赖的属性或数据变化时,会运行一个函数,该函数会做两件事: 运行_render生成一棵新的虚拟dom树(vnode tree),返回根节点运行_update,传入虚拟dom树的根节点,对新旧…...
Spring Boot 3.x 与 Spring Boot 2.x 的对比
Spring Boot 是 Java 开发领域的一个重要框架,它简化了基于 Spring 的应用开发。随着版本的不断更新,Spring Boot 提供了更多功能、更好的性能以及更简洁的配置。本文将详细对比 Spring Boot 3.x 和 Spring Boot 2.x,探讨它们之间的主要区别和…...
SSLError ClosedPoolError
分析日志 从您提供的日志文件内容来看,存在几个明显的问题导致了实例无法创建: SSL证书验证失败:日志中多次出现SSLError(SSLError(1, [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:897)),)错误。这表明客户端在尝试…...
勒索软件分析_Conti
0. Conti介绍 勒索软件即服务(Ransomware as a Service,RaaS)变体 Conti 推出还不到两年,已经进行了第七次迭代。Conti被证明是一种敏捷而熟练的恶意软件威胁,能够自主和引导操作,并具有无与伦比的加密速度…...
Linux系统如何通过编译方式安装python3.11.3
1.切换到/data 目录 cd /data 2.下载python源码Python-3.11.3.tgz wget https://www.python.org/ftp/python/3.11.3/Python-3.11.3.tgz tar -xzf Python-3.11.0.tgz cd Python-3.11.3 3.配置python的安装路径 和 执行openssl的路径 ./configure --prefix/usr/local/pyth…...
仿《Q极速体育》NBACBA体育直播吧足球直播综合体育直播源码
码名称:仿《Q极速体育》NBACBA体育直播吧足球直播综合体育直播源码 开发环境:帝国cms7.5 空间支持:phpmysql 仿《Q极速体育》NBACBA体育直播吧足球直播综合体育直播源码自动采集 - 我爱模板网源码名称:仿《Q极速体育》NBACBA体育直…...
代码随想录算法训练营第四天| 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点 、 面试题 02.07. 链表相交、142.环形链表II
24. 两两交换链表中的节点 题目链接: 24. 两两交换链表中的节点 文档讲解:代码随想录 状态:没做出来,没有正确更新头节点,因为head和cur共享引用,会随着cur的移动,丢失之前存放的节点 错误代码&…...
吉林大学计科21级《软件工程》期末考试真题
文章目录 21级期末考试题一、单选题(2分一个,十个题,一共20分)二、问答题(5分一个,六个题,一共30分)三、分析题(一个10分,一共2个,共20分…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...
SpringCloud优势
目录 完善的微服务支持 高可用性和容错性 灵活的配置管理 强大的服务网关 分布式追踪能力 丰富的社区生态 易于与其他技术栈集成 完善的微服务支持 Spring Cloud 提供了一整套工具和组件来支持微服务架构的开发,包括服务注册与发现、负载均衡、断路器、配置管理等功能…...
[KCTF]CORE CrackMe v2.0
这个Reverse比较古老,已经有20多年了,但难度确实不小。 先查壳 upx压缩壳,0.72,废弃版本,工具无法解压。 反正不用IDA进行调试,直接x32dbg中,dump内存,保存后拖入IDA。 这里说一下…...
湖北理元理律师事务所:债务清偿方案中的法律技术革新
文/金融法律研究组 当前债务服务市场存在结构性矛盾:债权人追求快速回款,债务人需要喘息空间。湖北理元理律师事务所通过创新法律技术,在《企业破产法》《民法典》框架下构建梯度清偿模型,实现多方利益平衡。 一、个人债务优化的…...
Spring Boot 中实现 HTTPS 加密通信及常见问题排查指南
Spring Boot 中实现 HTTPS 加密通信及常见问题排查指南 在金融行业安全审计中,未启用HTTPS的Web应用被列为高危漏洞。通过正确配置HTTPS,可将中间人攻击风险降低98%——本文将全面解析Spring Boot中HTTPS的实现方案与实战避坑指南。 一、HTTPS 核心原理与…...
JavaScript性能优化实战大纲
性能优化的核心目标 降低页面加载时间,减少内存占用,提高代码执行效率,确保流畅的用户体验。 代码层面的优化 减少全局变量使用,避免内存泄漏 // 不好的实践 var globalVar I am global;// 好的实践 (function() {var localV…...

即使类没有显示定义特殊成员函数,如果类中有虚函数或虚基类,它也不是平凡类型。
类中如果有动态内存分配(如指针成员)或需要特殊资源管理的成员,也不是平凡类型。
平凡类型的所有特殊成员函数都是平凡的,这意味着它们可以没有函数体(即使用编译器提供的默认实现)。