深入剖析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://www.mzph.cn/diannao/45085.shtml

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

相关文章

Qt/C++项目积累: 2.主机监控器 - 2.2 历史功能实现

修订历史&#xff1a; 20240711&#xff1a;初始表设计&#xff0c;采用sqlite 正文&#xff1a; 关于历史数据存储&#xff0c;考虑的是用数据库来完成&#xff0c;目前考虑使用Sqlite和mysql&#xff0c;先用sqlite来实现&#xff0c;设计表过程如下&#xff1a; 机器总览…

全网最适合入门的面向对象编程教程:14 类和对象的 Python 实现-类的静态方法和类方法,你分得清吗?

全网最适合入门的面向对象编程教程&#xff1a;14 类和对象的 Python 实现-类的静态方法和类方法&#xff0c;你分得清吗&#xff1f; 摘要&#xff1a; 本文主要介绍了Python中类和对象中的类方法和静态方法&#xff0c;以及类方法和静态方法的定义、特点、应用场景和使用方…

网安防御保护-小实验

1、DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00-18:00)可以访问&#xff0c;生产区的设备全天可以访问 2、生产区不允许访问互联网&#xff0c;办公区和游客区允许访问互联网 3、办公区设备10.0.2.10不允许访问DMZ区的FTP服务器和HTTP服务器&#xff0c;仅能ping通…

自主研发接口测试框架

测试任务&#xff1a;将以前完成的所有的脚本统一改写为unitest框架方式 1、需求原型 1.1 框架目录结构 V1.0&#xff1a;一般的设计思路分为配置层、脚本层、数据层、结果层&#xff0c;如下图所示 V 2.0&#xff1a;加入驱动层testdriver 1.2 框架各层需要完成的工作 1、配…

Fast DDS library windows 下源码编译(cmake)

目录 编译环境&#xff1a; 编译需要的源码文件&#xff1a; Fast DDS编译&#xff1a; 注意事项&#xff1a; 参考文档&#xff1a; 基于Fast DDS 的源码来编译相关的库&#xff0c;然后可以通过python 来调用库文件实现dds 数据通信&#xff0c;本文就详细的介绍编译过程…

机器学习筑基篇,容器调用显卡计算资源,Ubuntu 24.04 快速安装 NVIDIA Container Toolkit!...

[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ] Ubuntu 24.04 安装 NVIDIA Container Toolkit 什么是 NVIDIA Container Toolkit? 描述:NVIDIA Container Toolkit(容器工具包)使用户能够构建和运行 GPU 加速的容器,该工具包括一个容器运行时库和实用程序,用于自动…

石油巨头受冲击!埃克森美孚、BP接连发出盈利预警

KlipC报道&#xff1a;近日&#xff0c;BP&#xff08;英国石油&#xff09;预计其第二季度将面临10亿至20亿美元的减值费用&#xff0c;并发出警告称其炼油利润率“大幅下降”&#xff0c;石油交易收益预计出现疲软。消息公布后&#xff0c;其股价下跌超4%。 由于中间馏分油利…

JavaScript(8)——函数

函数 function,是被设计执行特定任务的代码块。 函数可以把具有相同或相似逻辑的代码包裹起来&#xff0c;通过函数调用执行这些代码&#xff0c;这么做的优势有利于精简代码方便复用。类似于alert(),prompt()和console.log()&#xff0c;这些都是js函数&#xff0c;不过已经…

STL(一)

书写形式&#xff1a;string (const string& str, size_t pos, size_t len npos); 举例&#xff1a; int main(){ string url("https://mp.csdn.net/mp_blog/creation/editor?spm1000.2115.3001.4503") string sub1(url,0,5);//从下标为0开始向后5个字符&…

如何在 Python 中创建一个类似于 MS 计算器的 GUI 计算器

问题背景 假设我们需要创建一个类似于微软计算器的 GUI 计算器。这个计算器应该具有以下功能&#xff1a; 能够显示第一个输入的数字。当按下运算符时&#xff0c;输入框仍显示第一个数字。当按下第二个数字时&#xff0c;第一个数字被替换。 解决方案 为了解决这个问题&am…

Spring中的适配器模式和策略模式

1. 适配器模式的应用 1.1适配器模式&#xff08;Adapter Pattern&#xff09;的原始定义是&#xff1a;将一个类的接口转换为客户期望的另一个接口&#xff0c;适配器可以让不兼容的两个类一起协同工作。 1.2 AOP中的适配器模式 在Spring的AOP中&#xff0c;使用Advice&#…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第一篇 嵌入式Linux入门篇-第十九章 Linux 工具之make 工具和 makefile 文件

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

集群管理脚本

虚拟机集群管理脚本 文章目录 虚拟机集群管理脚本一、远程调用脚本(remote_call.sh)二、远程复制目录脚本(remote_copy.sh) 一、远程调用脚本(remote_call.sh) 如果有传命令参数&#xff0c;则执行该命令&#xff1b;如果没有传命令参数&#xff0c;则不执行。 #!/bin/bashcm…

【嵌入式Linux】<知识点> GDB调试(更新中)

文章目录 前言 一、GDB调试预备工作 二、GDB的启动与退出 三、GDB中查看源代码 四、GDB断点操作 五、GDB调试指令 前言 在专栏【嵌入式Linux】应用开发篇_Linux打工仔的博客中&#xff0c;我们已经写了大量的源程序。但是在调试这些程序时我们都是通过printf大法和肉眼除…

评估指标:精确率(Precision)、召回率(Recall)、F1分数(F1 Score)

评估指标&#xff1a;精确率&#xff08;Precision&#xff09;、召回率&#xff08;Recall&#xff09;、F1分数&#xff08;F1 Score&#xff09; 前言相关介绍1. 准确率&#xff08;Accuracy&#xff09;2. 精确率&#xff08;Precision&#xff09;3. 召回率&#xff08;Re…

Cadence23打开与关闭飞线,修改位号丝印大小

打开与关闭所有飞线&#xff1a; 显示部分飞线&#xff1a; 单独显示网络飞线尤为好用&#xff0c;点击上图中的网络&#xff0c;之后鼠标点击器件中你想高亮的网络即可单独打开部分飞线。 这里的关闭部分网络的飞线也很好用&#xff0c;可以临时关闭讨厌的GND飞线&#xff1a…

virturalBox+K8S部署jaeger-all-in-one

pod的yaml如下&#xff1a;这里使用的是主机host模式 apiVersion: apps/v1 kind: Deployment metadata:name: jaegerlabels:app: jaeger spec:replicas: 1selector:matchLabels:app: jaegertemplate:metadata:labels:app: jaegerspec:hostNetwork: truecontainers:- name: jae…

TF卡病毒是什么?如何防范和应对?

在存储芯片及存储卡领域&#xff0c;TF卡病毒是一个备受关注的话题。在本文中&#xff0c;拓优星辰将详细解释TF卡病毒的含义、来源以及如何防范和应对这一问题&#xff0c;帮助客户更好地了解和处理TF卡病毒的风险。 1. TF卡病毒的含义 TF卡病毒是指针对TF存储卡&#xff08;T…

05:定时器中断

中断 1、定时器T0中断2、案例&#xff1a;通过定时器T0中断来实现灯间隔1s亮灭 1、当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求&#xff0c;要求CPU暂停当前的工作&#xff0c;转而去处理这个紧急事件&#xff0c;处理完以后&#xff0c;再回到原来被中断的地方…

【数据结构】一文了解七大排序算法

文章目录 前言一.直接插入排序插入排序思想插入排序代码实现插入排序总结 二.希尔排序希尔排序思想希尔排序代码实现希尔排序总结 三.选择排序选择排序思想选择排序代码实现选择排序总结 四.堆排序堆排序思想堆排序代码实现堆排序总结 五、冒泡排序冒泡排序思想冒泡排序代码实现…