C++之std::is_trivially_copyable(平凡可复制类型检测)

news2024/12/23 21:20:11

目录

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; //输出:true
    std::cout << "NonTrivialType1 is trivially:" 
              << std::is_trivially<NonTrivialType1>::value << std::endl; //输出:false

    std::cout << "TrivialType2is trivially:" 
              << std::is_trivially<TrivialType2>::value << std::endl; //输出: false
    return 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 copyable
    A() {}                             // trivially copyable
    A(const A &) = default;            // trivially copyable
    A(A &&) = default;                 // trivially copyable
    A &operator=(const A &) = default; // trivially copyable
    A &operator=(A &&) = default;      // trivially copyable
};

class B
{
    // 只要有任意自定义的下列行为即会变成 not trivially copyable
    virtual 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 copyable
    cout << std::is_trivially_copyable<char>::value << endl;           // trivially copyable
    cout << std::is_trivially_copyable<int>::value << endl;            // trivially copyable
    cout << std::is_trivially_copyable<float>::value << endl;          // trivially copyable
    cout << std::is_trivially_copyable<double>::value << endl;         // trivially copyable
    cout << std::is_trivially_copyable<std::nullptr_t>::value << endl; // trivially copyable
    cout << std::is_trivially_copyable<int *>::value << endl;          // trivially copyable
    cout << std::is_trivially_copyable<A>::value << endl;              // trivially copyable
    cout << std::is_trivially_copyable<A *>::value << endl;            // trivially copyable
    cout << std::is_trivially_copyable<B>::value << endl;              // not trivially copyable
    cout << std::is_trivially_copyable<B *>::value << endl;            // trivially copyable
    cout << std::is_trivially_copyable<C>::value << endl;              // not trivially copyable
    cout << 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(平凡的数据)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1698246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入理解与防御跨站脚本攻击(XSS):从搭建实验环境到实战演练的全面教程

跨站脚本攻击&#xff08;XSS&#xff09;是一种常见的网络攻击手段&#xff0c;它允许攻击者在受害者的浏览器中执行恶意脚本。以下是一个XSS攻击的实操教程&#xff0c;包括搭建实验环境、编写测试程序代码、挖掘和攻击XSS漏洞的步骤。 搭建实验环境 1. 安装DVWA&#xff…

8.什么是HOOK

程序编译的本质是&#xff0c;首先计算机它只能看得懂机器码也就是只能看得懂数字&#xff0c;机器码学起来很费劲然后就创造了编译器这个东西&#xff0c;编译器它懂机器语言所以它可以跟机器沟通&#xff0c;而我们人可以跟编译器沟通&#xff0c;人跟编译器的语言就是各种各…

GBDT、XGBoost、LightGBM算法详解

文章目录 一、GBDT (Gradient Boosting Decision Tree) 梯度提升决策树1.1 回归树1.2 梯度提升树1.3 Shrinkage1.4 调参1.5 GBDT的适用范围1.6 优缺点 二、XGBoost (eXtreme Gradient Boosting)2.1 损失函数2.2 正则项2.3 打分函数计算2.4 分裂节点2.5 算法过程2.6 参数详解2.7…

不拍视频,不直播怎么在视频号卖货赚钱?开一个它就好了!

大家好&#xff0c;我是电商糖果 视频号这两年看着抖音卖货的热度越来越高&#xff0c;也想挤进电商圈。 于是它模仿抖音推出了自己的电商平台——视频号小店。 只要商家入驻视频号小店&#xff0c;就可以在视频号售卖商品。 具体怎么操作呢&#xff0c;需要拍视频&#xf…

leedcode【142】. 环形链表 II——Java解法

Problem: 142. 环形链表 II 思路解题方法复杂度Code性能 思路 1.用快慢指针找到相遇的点&#xff08;快指针一次走一步&#xff0c;慢指针一次走两步&#xff09; 2.一个指针从head开始&#xff0c;一个指针从相遇点开始&#xff0c;一次一步&#xff0c;相遇处即为环入口 解题…

Vmware 17安装 CentOS9

前言 1、提前下载好需要的CentOS9镜像&#xff0c;下载地址&#xff0c;这里下载的是x86_64 2、提前安装好vmware 17&#xff0c;下载地址 &#xff0c;需要登录才能下载 安装 1、创建新的虚拟机 2、在弹出的界面中选择对应的类型&#xff0c;我这里选择自定义&#xff0c;点…

P459 包装类Wrapper

包装类的分类 1&#xff09;针对八种基本数据类型相应的引用类型——包装类。 2&#xff09;有了类的特点&#xff0c;就可以调用类中的方法。 Boolean包装类 Character包装类 其余六种Number类型的包装类 包装类和基本数据类型的相互转换 public class Integer01 {publi…

【大数据面试题】32 Flink 怎么重复读 Kafka?

一步一个脚印&#xff0c;一天一道面试题 首先&#xff0c;为什么要读过的 Kafka 数据重写读一次呢&#xff1f;什么场景下要怎么做呢&#xff1f; 答&#xff1a;当任务失败&#xff0c;从检查点Checkpoint 开始重启时&#xff0c;检查点的数据是之前的了&#xff0c;就需要…

从 0 开始实现一个网页聊天室 (小型项目)

实现功能 用户注册和登录好友列表展示会话列表展示: 显示当前正在进行哪些会话 (单聊 / 群聊) , 选中好友列表中的某个好友, 会生成对应的会话实时通信, A给B发送消息, B的聊天界面 / 会话界面能立刻显示新的消息 TODO: 添加好友功能用户头像显示传输图片 / 表情包历史消息搜…

CTF之Web_python_block_chain

这种题对于我来说只能看大佬的wp&#xff08;但是这一题是wp都看不懂&#xff0c;只能表达一下我的理解了&#xff09; &#xff08;最后有简单方法&#xff0c;前面一种没看懂没关系&#xff09; 下面这一部分是首页的有用部分 访问/source_code,得到源码&#xff1a; # -*-…

鸿蒙 DevEco Studio 3.1 Release 下载sdk报错的解决办法

鸿蒙 解决下载SDK报错的解决方法 最近在学习鸿蒙开发&#xff0c;以后也会记录一些关于鸿蒙相关的问题和解决方法&#xff0c;希望能帮助到大家。 总的来说一般有下面这样的报错 报错一&#xff1a; Components to install: - ArkTS 3.2.12.5 - System-image-phone 3.1.0.3…

企业客户信息反馈|基于SprinBoot+vue的企业客户信息反馈平台(源码+数据库+文档)

企业客户信息反馈平台 目录 基于SprinBootvue的企业客户信息反馈平台 一、前言 二、系统设计 三、系统功能设计 1平台功能模块 2后台登录 5.2.1管理员功能 5.2.2客户功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&am…

AIGC 005-Dreambooth定制化生成,微调文本到图像的扩散模型!

AIGC 005-Dreambooth定制化生成&#xff0c;微调文本到图像的扩散模型&#xff01; 文章目录 0 论文工作1 论文方法2 效果 0 论文工作 DreamBooth 论文 (DreamBooth: Fine-Tuning Text-to-Image Diffusion Models for Subject-Driven Generation) 提出了一种新颖的技术&#x…

2024GDCPC广东省赛记录

比赛流程体验&#xff0c;依托&#xff0c;开赛几分钟了&#xff0c;选手还卡在门外无法入场&#xff0c;也没给延时&#xff0c;说好的桌上会发三支笔&#xff0c;于是我们就没准备&#xff0c;要了三次笔&#xff0c;终于在一小时后拿到了&#x1f605; 比赛题目体验&#xf…

近期阅读论文

Exploring Hybrid Active-Passive RIS-Aided MEC Systems: From the Mode-Switching Perspective abstract 移动边缘计算&#xff08;MEC&#xff09;被认为是支持延迟敏感和计算密集型服务的有前途的技术。 然而&#xff0c;随机信道衰落特性导致的低卸载率成为制约MEC性能的…

数据结构篇之二叉树(binary tree)的介绍和应用

欢迎光临&#xff1a; 男神 目录 一树的介绍和表示&#xff1a; 二二叉树的介绍及性质&#xff1a; 三堆的介绍及创建&#xff1a; 1堆的创建&#xff1a; 2堆的应用&#xff1a; 四二叉树的创建&#xff1a; ①// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二…

Qt 对话框或者QMainWindow等类中调用自定义QWidget继承组件

简单的方法如下所示 1、创建一个ui文件&#xff0c;界面布局放入QVBoxLayout或者QHBoxLayout 使用他来放入自定义组件&#xff0c;类似如下 2、代码如下&#xff1a; ui.setupUi(this); { //自定义组价如下 KwTable *Table new KwTable(this); ui.vertical…

Firefox国际版

Firefox国际版官方网址&#xff1a; Download the Firefox Browser in English (US) and more than 90 other languagesEveryone deserves access to the internet — your language should never be a barrier. That’s why — with the help of dedicated volunteers around…

从用法到源码再到应用场景:全方位了解CompletableFuture及其线程池

文章目录 文章导图什么是CompletableFutureCompletableFuture用法总结API总结 为什么使用CompletableFuture场景总结 CompletableFuture默认线程池解析&#xff1a;ForkJoinPool or ThreadPerTaskExecutor&#xff1f;ForkJoinPool 线程池ThreadPerTaskExecutor线程池Completab…

【网络协议】应用层协议--HTTP

文章目录 一、HTTP是什么&#xff1f;二、HTTP协议工作过程三、HTTP协议1. fiddler2. Fiddler抓包的原理3. 代理服务器是什么?4. HTTP协议格式1.1 请求1.2 响应 四、认识HTTP的请求1.认识HTTP请求的方法2.认识请求头&#xff08;header&#xff09;3.认识URL3.1 URL是什么&…