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分…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

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