深入剖析C++的 “属性“(Attribute specifier sequence)

引言

在阅读开源项目源代码是,发现了一个有趣且特殊的C++特性:属性。

属性(attribute specifier sequences)是在C++11标准引入的。在C++11之前,编译器特有的扩展被广泛用来提供额外的代码信息。例如,GNU编译器(GCC)使用__attribute__来控制函数的行为。但是缺点也很明显,那就是这种方式缺乏标准化,不同编译器的特性并不兼容,严重影响了代码的可移植性。

为了解决这一问题,C++11标准引入了属性,提供一种标准化的方法来给编译器额外的信息。属性既可以应用于类型声明,也可以应用于声明语句、定义、代码块等,为编译器提供了关于编码意图的附加信息。

属性的作用

属性引入C++后,提供了以下几个方面的价值:

  1. 可移植性:提供了一种标准的方式来传达编译器特定的信息,减少了依赖特定编译器扩展的需要。

  2. 可读性:通过标准的属性,代码的特定行为和意图被清晰地表达,易于他人理解。

  3. 健壮性:属性可以帮助检测潜在的错误,如忽略了重要的函数返回值。

  4. 性能:许多属性可以帮助编译器进行优化,提高程序性能。

常见属性

以下是C++中一些常用的属性:

  1. [[noreturn]] - 表明函数不会返回到调用者。这通常用于那些通过抛出异常或终止程序来退出的函数。

  2. [[nodiscard]] - 用于函数或类型,标明调用者不应忽略返回值。对于类型,它表示该类型的对象或其构造函数返回的对象不应被忽略。

  3. [[deprecated]] - 标记某个实体(如函数、类、类型别名、变量等)为过时的,建议不要使用。可选地,可以提供一条消息说明替代的使用方式。

  4. [[fallthrough]] - 在C++17中引入,用于switch语句的case节中,明确表示允许控制流从当前case分支无条件跳转到下一个case分支的行为,避免编译器生成可能的警告。

  5. [[maybe_unused]] - 表示某个实体(如函数、类、变量等)可能不会被使用,从而防止编译器发出未使用警告。

  6. [[likely]][[unlikely]] - 这两个属性是在C++20中引入的,用来显式地指示给定的布尔表达式结果的可能性。[[likely]]表示表达式结果为true的可能性更高,而[[unlikely]]表示结果为false的可能性更高。

  7. [[no_unique_address]] - 在C++20中引入,它指示类成员不必拥有唯一的地址,允许空基优化或类似的优化技术。

这些属性可以用来改善代码的文档化、提高性能、帮助编译器检查错误和警告,以及启用或禁用特定的编译器行为。
它们是现代C++编程中代码质量和维护方面的重要工具。不同的编译器可能对属性的支持程度有所不同。

常用属性示例

[[noreturn]]

[[noreturn]]属性指示某个函数不会返回到调用者。这通常用于那些通过抛出异常或终止程序来退出的函数。

[[noreturn]] void terminate_program() {// ... 清理操作 ...std::exit(1); // std::exit不返回
}

[[nodiscard]]

[[nodiscard]]属性用于函数或类型,标明调用者不应忽略返回值。这有助于防止编码中的错误,例如忘记处理函数返回的错误代码。

[[nodiscard]] int compute() {// ... 计算操作 ...return result;
}void example() {compute(); // 如果忽略了结果,编译器可能会警告
}

C++20对nodiscard进行了扩展,支持注明原因。格式为:[[nodiscard(“reason”)]]

[[deprecated]]

[[deprecated]]属性用于标记过时的实体。

[[deprecated("Use new_function instead")]]
void old_function() {// ...
}void example() {old_function(); // 使用这个函数时,编译器会给出警告
}

[[fallthrough]]

[[fallthrough]]属性用在switch语句中,表示有意识的case穿透。

void example(int val) {switch (val) {case 1:// 计算某些事情[[fallthrough]]; // 明确表明穿透是有意为之case 2:// 1和2执行相同的代码break;default:// 其他值处理break;}
}

[[maybe_unused]]

[[maybe_unused]]属性用于可能不使用的变量或函数,防止编译器发出未使用警告。

[[maybe_unused]] static bool is_debug = true;void example() {[[maybe_unused]] int local_variable = compute();
}

[[likely]] 和 [[unlikely]]

[[likely]][[unlikely]]在C++20中引入,帮助编译器优化分支预测。

bool condition = /* ... */;
if ([[likely]] condition) {// 大多数情况下,条件为真时的代码
} else {// 条件为假时的代码
}

其他不常用的属性介绍

[[carries_dependency]] (C++11)

[[carries_dependency]]属性用来指示在函数参数或返回值中的依赖关系链可以传递,这在使用std::memory_order时或在多线程编程中非常重要。

#include <atomic>
#include <iostream>std::atomic<int> global_data;// 该函数表明参数和返回值带有依赖关系链
[[carries_dependency]] int load_data(std::atomic<int>& data) {return data.load(std::memory_order_consume);
}void process_data() {// 从全局数据载入的依赖关系被保留int local_data = load_data(global_data);// 接下来的操作可以依赖于这个顺序
}

[[no_unique_address]] (C++20)

[[no_unique_address]]属性表示非静态数据成员不必具有唯一的地址,这允许编译器在可能的情况下进行内存优化。在C++中,即使是完全空的类(不含任何成员变量或成员函数)也至少会占用1字节的大小,这是为了确保每个对象都有一个唯一的地址。但是,有时候这个额外的1字节并不是必须的,例如当空类作为其他类的成员时,这在包含多个空类成员的大型结构体中可以节省大量内存。

struct Empty {}; // empty classstruct X
{int i;Empty e;
};struct Y
{int i;[[no_unique_address]] Empty e;
};int main()
{// the size of any object of empty class type is at least 1static_assert(sizeof(Empty) >= 1); // at least one more byte is needed to give e a unique addressstatic_assert(sizeof(X) >= sizeof(int) + 1); static_assert(sizeof(Y) == sizeof(int)); 
}

[[assume(expression)]] (C++23)

[[assume(expression)]]属性告诉编译器,在给定的点上,表达式总是评估为真。这有助于编译器进行优化。

void process_data(int* ptr) {// 告诉编译器ptr不为nullptr,这有助于优化[[assume(ptr != nullptr)]]*ptr = 42;
}

[[indeterminate]] (C++26)

[[indeterminate]]属性是一个假设的属性,用来指明一个对象如果没有被初始化,则具有不确定的值。由于C++26还目前还未发布,暂时用不了。

void f(int); 
void g()
{int x [[indeterminate]]; // indeterminate valueint y;                   // erroneous valuef(x); // 允许编译通过,但是具有不确定的行为f(y); // 编译报错,不允许使用未初始化的值
}

[[optimize_for_synchronized]] (TM TS)

[[optimize_for_synchronized]]属性来自事务性内存技术规范(TM TS),它建议函数应该为同步块中的调用进行优化。TM TS不是ISO C++标准的一部分,支持度因编译器而异。

// 这个属性建议函数在同步块中调用时应该进行优化
[[optimize_for_synchronized]] void synchronized_function() {// 在同步语句中频繁调用的函数
}

结语

笔者最喜欢的C++属性就是[[nodiscard]]了,计划今天就在团队中推广开。因为许多开发者在调用一些可能失败的函数不检查返回值,导致代码鲁棒性较低。给一些重要函数加上[[nodiscard]]属性之后,编译器就能避免这种情况的发生,真是太有用了。试了一下,MSVC下nodiscard会出现警告,还好我们开了警告视为错误,那么就可以确保开发者不会忘记处理返回值了。
在这里插入图片描述

如果向了解更多关于C++属性的知识,那么可以来cppreference看看。cppreference的C++的属性参考:Attribute specifier sequence(since C++11)

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

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

相关文章

无菌隔离器内操作规范性的验证之气流流型验证-北京中邦兴业

无菌隔离器在制药行业的使用愈加广泛&#xff0c;但已有的研究更多地聚焦于设计布局、物料状态等方面&#xff0c;对人员操作因素的影响方面关注较少。以冻干制剂生产车间为例&#xff0c;设计了一系列合理的无菌隔离器内干预操作&#xff0c;并在操作人员实行干预操作的基础上…

面试题 21:解释 Python 中的 help() 函数和 dir() 函数?

欢迎莅临我的博客 &#x1f49d;&#x1f49d;&#x1f49d;&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

8.6结构体函数参数

代码 #include <iostream> using namespace std; #include <string>//结构体函数参数//定义学生结构体 struct student {string name;int age;int score; };//打印学生信息的函数 //1、值传递 void printStudent1(struct student s) {cout << "子函数1…

如何查看GD32 Keil和IAR工程的map文件

我们在设计调试程序时&#xff0c;往往需要知道一个函数或一个变量它在MCU中具体所在的地址以及所占用的空间大小&#xff0c;这时候就需要查看map文件。 那么什么是map文件呢&#xff1f;map文件是编译器编译工程后生成的一个文件&#xff0c;文件会有很多信息&#xff0c;比…

小米恢复联系人,跟着这2个步骤,让你的社交重回巅峰

当你突然发现小米手机里的联系人列表变得空空如也&#xff0c;是不是感觉就像失去了与外界沟通的“秘密武器”&#xff1f;别担心&#xff0c;这并不意味着你真的失去了他们。他们可能只是藏在了手机里的某个神秘角落&#xff0c;等待着你的召唤。接下来&#xff0c;小编将会介…

连续6年夺冠 6项细分领域第一,中电金信持续领跑中国银行业IT解决方案市场

7月9日&#xff0c;工信部赛迪顾问发布《2023年度中国银行业IT解决方案市场分析报告》&#xff08;简称《报告》&#xff09;。中电金信以7.38%的市场份额再度蝉联2023中国银行业IT解决方案市场份额第一&#xff0c;以显著优势持续领跑中国银行业IT解决方案市场。在细分领域&am…

园区电表4G/Lora远程无线通讯-安科瑞自助缴费系统

项目案例&#xff1a;张江高科产业园 背景 上海张江高科技园区自1992年成立以来&#xff0c;经过近二十年的开发&#xff0c; 园区构筑了生物医药创新链&#xff0c;集成电路产业链和软件产业链的框架。园区建有国家上海生物医药科技产业基地、国家信息产业基地、国家集成电路…

[leetcode] car-pooling 拼车

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool carPooling(vector<vector<int>>& trips, int capacity) {int to_max 0;for (const auto& trip: trips) {to_max max(to_max, trip[2]);}vector<int> diff(to_max 1);for…

CentOS 设置手动同步指定NTP时钟服务器

1. 文件上传至服务器 2.进入文件路径 3.查看文件名称 4.安装rpm包 注意执行顺序 1&#xff0c;3&#xff0c;2 5.启动ntp并设置开机自启 6.配置ntp配置文件 修改为时间服务器ip&#xff08;同时要删除或注释掉其他三个server开头的外网的配置&#xff0c;防止因为外网不通导致…

电脑怎样连接打印机?详细步骤告诉你!

在现代办公环境中&#xff0c;打印机是必不可少的设备之一。无论是打印文档、图片还是其他资料&#xff0c;连接打印机都是我们日常工作中的基本操作。然而&#xff0c;不同类型的打印机和连接方式可能会让人感到困惑。本文将介绍电脑怎样连接打印机的3种方法&#xff0c;帮助您…

捷配笔记-如何设计PCB板布线满足生产标准?

PCB板布线是铺设连接各种设备与通电信号的路径的过程。PCB板布线是铺设连接各种设备与通电信号的路径的过程。 在PCB设计中&#xff0c;布线是完成产品设计的重要步骤。可以说&#xff0c;之前的准备工作已经为它做好了。在整个PCB设计中&#xff0c;布线设计过程具有最高的极限…

基于Android平台开发,仿头条新闻app

1. 项目模块功能思维导图 2. 项目涉及到的技术点 数据来源&#xff1a;聚合数据API使用okhttp网络请求框架获取api数据使用gson库解析json数据使用RecyclerViewadapter实现新闻列表使用SQLite数据库实现用户登录&#xff0c;注册&#xff0c;浏览历史记录使用SharedPreference…

【Python】基础语法体系:break,continue,pass语句详讲

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言break 语句实例 continue 语句实例 pass 语句实例 前言 接着上一章的知识&#xff0c;我们这一章继续来讲讲语句的相关知识&#xff0c;在Python中&#xff0c;break、contin…

数据库|实践干货!实现tiup与prometheus迁移

一、背景 由于规划变动&#xff0c;需要将tiup和prometheus移动到其他的机器上&#xff0c;要求平滑迁移&#xff0c;不丢失监控数据。 关于prometheus的数据迁移《迁移prometheus数据》&#xff08;https://tidb.net/blog/1ea36c1f?shareIdba5da793&#xff09;这篇专栏文章…

关于《中国PostgreSQL考试认证体系》通知

为响应国家大数据战略发展的号召&#xff0c;进一步推动PostgreSQL开源数据库在国内的快速发展&#xff0c;加强PostgreSQL 数据库相关人才培养&#xff0c;由政府相关部门批准发起&#xff0c;组成中国PostgreSQL认证考试中心及PostgreSQL 中国大学&#xff0c;旨在共同规范和…

MUNIK解读ISO26262 : 硬件架构评估及FMEDA(系统级)

前言 功能安全领域硬件层面的核心安全活动---FMEDA&#xff08;Failure Modes Effects and Diagnostic Analysis&#xff09;一直受到功能安全工程师的广泛关注&#xff01;作为定量分析的安全分析方法&#xff0c;FMEDA涉及到了复杂的计算公式和大范围的数据处理。 为何做FME…

Python 中什么是递归函数,如何编写递归函数?

递归是计算机科学中的一种基本概念&#xff0c;它指的是函数调用自身的编程技巧。在Python中&#xff0c;递归函数是一种通过调用自身来解决问题的函数。这种方法常用于解决可以被分解为较小相同问题的场景&#xff0c;例如阶乘计算、斐波那契数列、全排列生成等。 一、递归的…

Clustalw/Clustalx使用过程中需要注意的问题——待补充

序列比对软件clustalx无法加载序列 路径中不能有中文字符关于下载Index of /download/current 还是下载windows版本的clustalx吧&#xff0c;linux的不好安装。