(C++) 属性说明符-标准属性

文章目录

  • 前言
  • 标准属性
    • 🏷️noreturn
      • ⭐(C++11) 指示函数不返回
    • 🏷️carries_dependency
      • ⭐(C++11) 指示在函数内外传播“释放-消费” `std::memory_order` 中的依赖链
    • 🏷️deprecated
      • ⭐(C++14) 指示以此属性声明的名字或实体,允许使用但因某种 *原因* 而不鼓励使用
    • 🏷️fallthrough
      • ⭐(C++17) 指示从前一 case 标号的直落是故意的,且会警告直落的编译器不应当对此诊断
    • 🏷️nodiscard
      • ⭐(C++17/20) 鼓励编译器在返回值被丢弃时发出警告
    • 🏷️maybe_unused
      • ⭐(C++17) 抑制对于未使用实体的编译器警告,如果有
    • 🏷️likely & unlikely
      • ⭐(C++20) 指示编译器应当针对此种情况进行优化:通过某条语句的执行路径比其他任何执行路径更可能或不可能发生
    • 🏷️no_unique_address
      • ⭐(C++20) 指示一个非静态数据成员不必具有与类中的其他所有非静态数据成员都不同的地址
    • 🏷️assume(表达式)
      • ⭐(C++23) 指示 表达式 在给定的位置永远为 true
  • END

前言

cppref: 属性说明符序列(C++11 起) - cppreference.com

本文着重讲解属性说明符的标准属性

基础语法见:(C++) 属性说明符-语法

属性说明符是一种与具体编译器强绑定的内容。有的属性可以作为编译器的提示,有的则是直接作为设置。

在C++11之前,不同编译器拥有不同的语法,不同的说明符。在编写跨平台程序时非常麻烦,移植性一直不是很好。

到了C++11标准终于规定了属性说明符的标准。但是所支持的说明符随着标准的提出还不是很多。但该语法仍然允许使用编译器自身的说明符,为以后的扩展做了一定的预留。

标准属性

🏷️noreturn

⭐(C++11) 指示函数不返回

指示函数不返回。

void fun_0() {
}[[noreturn]] void fun_1() {
}int main() {
}

编译选项:-std=c++11 -O1

对应汇编 ,通过观察可以发现,没有标注[[noreturn]] 的函数还会会有对应的ret汇编。而标注了属性的函数则直接优化掉了。

fun_0():ret
fun_1():
main:movl    $0, %eaxret

🏷️carries_dependency

⭐(C++11) 指示在函数内外传播“释放-消费” std::memory_order 中的依赖链

指示释放消费 std::memory_order 中的依赖链传进和传出该函数,这允许编译器跳过不必要的内存栅栏指令。

对于本标签需要了解指令内存序的知识。

简单的说在多任务,多线程中为了保证资源内存访问的逻辑正确性,在生成对应的底层代码时会生成内存栅栏指令来维护正确性。

而这些生成的指令,肯定会消耗运行的效率。特别是在一些其实没必要保护的地方,就是多余的操作,该标签就是对应优化这种情况。

#include <atomic>
#include <cassert>
#include <iostream>void print_0(int* val) {std::cout << __LINE__ << " " << *val << std::endl;
}void print_1(int* val [[carries_dependency]]) {std::cout << __LINE__ << " " << *val << std::endl;
}int main() {int x{8172252};// 注意,不同编译器// 不同cpp标准的情况下// 使用等号初始化和直接初始化之间的兼容性std::atomic<int*> p{&x};// 直接等号是最强的内存序seq_cst,这里选用较弱的consumeint* local = p.load(std::memory_order_consume);assert(local);/*** 依赖是明确的,因而编译器已知 local 被解引用,* 且它必须保证保留依赖链,以避免(某些架构上的)一个内存栅栏。*/std::cout << __LINE__ << " " << *local << std::endl;/*** print_0 的定义不透明(假定它未被内联),* 因而编译器必须发出一个内存栅栏,* 以确保在 print_0 中读取 *p 返回正确值。*/print_0(local);/*** 编译器可以假定,虽然 print_1 也不透明,* 但指令流中仍保留了从形参到解引用值的依赖,* 且不需要(某些架构上的)内存栅栏。** 显然,print_1 的定义必须确实保留这项依赖,因而此属性也会影响* 为 print_1 所生成的代码。*/print_1(local);
}

本代码的汇编比较长,不直接展示了:对应汇编

🏷️deprecated

⭐(C++14) 指示以此属性声明的名字或实体,允许使用但因某种 原因 而不鼓励使用

指示声明有此属性的名字或实体被弃用,即允许但因故不鼓励使用。

deprecated 可以在使用时添加字符串字面量来辅助编程。

#include <iostream>void fun_no_deprecated() {std::cout << __func__ << std::endl;
}[[deprecated]]
void fun_0_deprecated() {std::cout << __func__ << std::endl;
}[[deprecated("Use NeogenePeriod() instead.")]]
void fun_1_deprecated() {std::cout << __func__ << std::endl;
}int main() {fun_no_deprecated();fun_0_deprecated();fun_1_deprecated();
}

在部分编译器,或者开启部分选项后可能会直接无法完成编译:

下面选用 gcc version 11.2.0 (x86_64-posix-seh-rev3, Built by MinGW-W64 project)的运行程序的输出:

main.cpp: In function 'int main()':
main.cpp:19:21: warning: 'void fun_0_deprecated()' is deprecated [-Wdeprecated-declarations]19 |     fun_0_deprecated();|     ~~~~~~~~~~~~~~~~^~
main.cpp:8:6: note: declared here8 | void fun_0_deprecated() {|      ^~~~~~~~~~~~~~~~
main.cpp:20:21: warning: 'void fun_1_deprecated()' is deprecated: Use NeogenePeriod() instead. [-Wdeprecated-declarations]20 |     fun_1_deprecated();|     ~~~~~~~~~~~~~~~~^~
main.cpp:13:6: note: declared here13 | void fun_1_deprecated() {|      ^~~~~~~~~~~~~~~~
fun_no_deprecated
fun_0_deprecated
fun_1_deprecated

🏷️fallthrough

⭐(C++17) 指示从前一 case 标号的直落是故意的,且会警告直落的编译器不应当对此诊断

指示从前一标号直落是有意的,而在发生直落时给出警告的编译器不应诊断它。

介绍一个switch的黑科技:(C/C++) 效率黑科技-Duff’s Device_duff device

void test(int n) {int cnt = 0;switch (n) {case 1:case 2:  // 直落时不警告cnt = 2;case 3:  // 编译器可在发生直落时警告cnt = 3;[[fallthrough]];  // 良构case 4:if (n < 3) {cnt = 4;[[fallthrough]];  // 良构} else {return;}case 5:while (false) {[[fallthrough]];  // 非良构:下一语句不是同一迭代的一部分}case 6:[[fallthrough]];  // 非良构:没有后继的 case 或 default 标号}
}

🏷️nodiscard

⭐(C++17/20) 鼓励编译器在返回值被丢弃时发出警告

若从并非转型到 void 的弃值表达式中,调用声明为 nodiscard 的函数,或调用按值返回声明为 nodiscard 的枚举或类的函数,则鼓励编译器发布警告。

/*** 给自定义类型指定*/
struct [[nodiscard]] Node {};Node test_self_type() {return {};
}/*** (C++20 起)* nodiscard(字符串字面量)*/
[[nodiscard("please do not discard")]]
int test_fun(int x, int y) {return x + y;
}int main() {// 虽然函数没标注// 但是因为返回值是一个标注了的类型// 因此可能有警告test_self_type();// 可能有警告test_fun(1, 2);// 正常codeint x = test_fun(3, 4);return x;
}

🏷️maybe_unused

⭐(C++17) 抑制对于未使用实体的编译器警告,如果有

抑制针对未使用实体的警告。

此属性可出现在下列实体的声明中:

  • class/struct/union:struct [[maybe_unused]] S;
  • typedef,包括别名声明:[[maybe_unused]] typedef S* PS;using PS [[maybe_unused]] = S*;
  • 变量,包括静态数据成员:[[maybe_unused]] int x;
  • 非静态数据成员:union U { [[maybe_unused]] int n; };
  • 函数:[[maybe_unused]] void f();
  • 枚举:enum [[maybe_unused]] E {};
  • 枚举项:enum { A [[maybe_unused]], B [[maybe_unused]] = 42 };
  • 结构化绑定:[[maybe_unused]] auto [a, b] = std::make_pair(42, 0.23);

对于声明为 [[maybe_unused]] 的实体,如果没有使用这些实体或它们的结构化绑定,那么编译器针对未使用实体发布的警告会被抑制。

#include <cassert>/*** class/struct/union*/
struct [[maybe_unused]] S {[[maybe_unused]] int x;
};/*** enum*/
enum [[maybe_unused]] E { A [[maybe_unused]], B [[maybe_unused]] = 42 };/*** typedef,包括别名声明*/
using Task [[maybe_unused]] = void (*)(void);/*** 未使用函数 “f”* 未使用参数 “thing1” 与 “thing2”*/
[[maybe_unused]]
void f([[maybe_unused]] bool thing1, [[maybe_unused]] bool thing2) {
/*** 未使用标签 “lb”*/
[[maybe_unused]] lb:/*** 发行模式中,assert 在编译中被去掉,因而未使用 “b”* 无警告,因为它被声明为 [[maybe_unused]]*/[[maybe_unused]] bool b = thing1 && thing2;assert(b);
}int main() {
}

🏷️likely & unlikely

⭐(C++20) 指示编译器应当针对此种情况进行优化:通过某条语句的执行路径比其他任何执行路径更可能或不可能发生

允许编译器为包含该语句的执行路径,比任何其他不包含该语句的执行路径,更可能或更不可能的情况进行优化。

using ll = long long;ll binPow(ll base, ll expo, ll mod) {ll ans = 1;base %= mod;if (expo == 0) [[unlikely]] {ans = 1 % mod;} else [[likely]] {while (expo) {if (expo & 1) {ans = ans * base % mod;}base = base * base % mod;expo >>= 1;}}return ans;
};#include <iostream>int main() {std::cout << binPow(2, 100, 1e9 + 7);
}

🏷️no_unique_address

⭐(C++20) 指示一个非静态数据成员不必具有与类中的其他所有非静态数据成员都不同的地址

允许此数据成员与其类的其他非静态数据成员或基类子对象重叠。

注意,一切以编译器实际情况为准!!!

#include <cstdint>
#include <iostream>#ifdef _MSC_VER
#define no_unique_address msvc::no_unique_address
#endif  // _MSC_VER/*** 空类* 任何空类类型对象的大小至少为 1*/
struct Empty {};/*** 按照默认方式对齐* 至少需要多一个字节以给 e 唯一地址*/
void test_1() {struct X {int32_t i;Empty   e;};std::cout << "size = " << sizeof(X) << '\n';
}/**** 优化掉空成员* 和前面的内存共用*/
void test_2() {struct Y {int32_t                     i;[[no_unique_address]] Empty e;};std::cout << "size = " << sizeof(Y) << '\n';
}/*** e1 与 e2 不能共享同一地址,* 因为它们拥有相同类型,* 尽管它们标记有 [[no_unique_address]]。* 然而,其中一者可以与 c 共享地址。*/
void test_3() {struct Z {uint8_t                     c;[[no_unique_address]] Empty e1, e2;};std::cout << "size = " << sizeof(Z) << '\n';
}/*** e1 与 e2 不能拥有同一地址,* 但它们之一能与 c[0] 共享,* 而另一者与 c[1] 共享*/
void test_4() {struct W {uint8_t                     c[2];[[no_unique_address]] Empty e1, e2;};std::cout << "size = " << sizeof(W) << '\n';
}int main() {// 任何空类类型对象的大小至少为 1static_assert(sizeof(Empty) >= 1);test_1();test_2();test_3();test_4();
}

🏷️assume(表达式)

⭐(C++23) 指示 表达式 在给定的位置永远为 true

指示表达式在给定位置总是求值为 true。

简单说,如果assume被通过为true,则下文的程序将可能被优化。否则行为未定义。

特别注意该表达式不会求值(但它依然会潜在求值)。

#include <cmath>void g(int) {
}
void h() {
}void f(int& x, int y) {/*** 编译器可以假设 x 是正数* 可以生成更高效的代码*/[[assume(x > 0)]];g(x / 2);x     = 3;int z = x;/*** 编译器可以假设在调用 h 后 x 的值保持相同* 该假设本身不会调用 h*/[[assume((h(), x == z))]];h();g(x);  // 编译器可以用 g(3); 替换该语句/*** 这里没有 assume* 或者说上面已经作用完上一个assume了* 编译器不能用 g(3); 替换该语句假设只在它出现的地方适用*/h();g(x);/*** @brief* 编译器可以假设 g(z) 会返回* 根据上面和下面的假设,编译器可以用 g(10); 替换该语句*/z = std::abs(y);[[assume((g(z), true))]];g(z);/*** 这里 y != -10 的情况下行为未定义*/[[assume(y == -10)]];/*** 编译器可以用 g(5); 替换该语句*/[[assume((x - 1) * 3 == 12)]];g(x);
}int main() {
}

目前所支持的编译器还很少(目前仅gcc13, msvc19): 测试效果

END

关注我,学习更多C/C++,算法,计算机知识

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

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

相关文章

机器学习之决策树现成的模型使用

目录 须知 DecisionTreeClassifier sklearn.tree.plot_tree cost_complexity_pruning_path(X_train, y_train) CART分类树算法 基尼指数 分类树的构建思想 对于离散的数据 对于连续值 剪枝策略 剪枝是什么 剪枝的分类 预剪枝 后剪枝 后剪枝策略体现之威斯康辛州乳…

Linux基础篇:解析Linux命令执行的基本原理

Linux 命令是一组可在 Linux 操作系统中使用的指令&#xff0c;用于执行特定的任务&#xff0c;例如管理文件和目录、安装和配置软件、网络管理等。这些命令通常在终端或控制台中输入&#xff0c;并以文本形式显示输出结果。 Linux 命令通常以一个或多个单词的简短缩写或单词…

学习vue3第十二节(组件的使用与类型)

1、组件的作用用途 目的&#xff1a; 提高代码的复用度&#xff0c;和便于维护&#xff0c;通过封装将复杂的功能代码拆分为更小的模块&#xff0c;方便管理&#xff0c; 当我们需要实现相同的功能时&#xff0c;我们只需要复用已经封装好的组件&#xff0c;而不需要重新编写相…

(九)图像的高斯低通滤波

环境&#xff1a;Windows10专业版 IDEA2021.2.3 jdk11.0.1 OpenCV-460.jar 系列文章&#xff1a; &#xff08;一&#xff09;PythonGDAL实现BSQ&#xff0c;BIP&#xff0c;BIL格式的相互转换 &#xff08;二&#xff09;BSQ,BIL,BIP存储格式的相互转换算法 &#xff08;三…

Flask学习(六):蓝图(Blueprint)

蓝图&#xff08;Blueprint&#xff09;&#xff1a;将各个业务进行区分&#xff0c;然后每一个业务单元可以独立维护&#xff0c;Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法&#xff0c;它并不是必须要实现应用的视图和函数的。 Demo目录结构&#xf…

linux离线安装jenkins及使用教程

本教程采用jenkins.war的方式离线安装部署&#xff0c;在线下载的方式会遇到诸多问题&#xff0c;不宜采用 一、下载地址 地址&#xff1a;Jenkins download and deployment 下载最新的长期支持版 由于jenkins使用java开发的&#xff0c;所以需要安装的linux服务器装有jdk环…

插入排序、归并排序、堆排序和快速排序的稳定性分析

插入排序、归并排序、堆排序和快速排序的稳定性分析 一、插入排序的稳定性二、归并排序的稳定性三、堆排序的稳定性四、快速排序的稳定性总结在计算机科学中,排序是将一组数据按照特定顺序进行排列的过程。排序算法的效率和稳定性是评价其优劣的两个重要指标。稳定性指的是在排…

新版Idea2023.3.5与lombok冲突、@Data失效

新版idea和lombok冲突&#xff0c;加上Data&#xff0c;其他地方get set也不报错&#xff0c;但是一运行就找不到get set方法。 但是直接使用Getter和Setter可以访问、应该是Data失效了。 解决方法&#xff1a; 看推上介绍是 lombok 与 idea 采集 get 、set 方法的时候所用的技…

Jupyter开启远程服务器(最新版)

Jupyter Notebook 在本地进行访问时比较简单&#xff0c;直接在cmd命令行下输入 jupyter notebook 即可&#xff0c;然而notebook的作用不止于此&#xff0c;还可以用于远程连接服务器&#xff0c;这样如果你有一台服务器内存很大&#xff0c;但是呢你又不喜欢在linux上进行操作…

【C语言】编译和链接----预处理详解【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本篇为【C语言】文件操作揭秘&#xff1a;C语言中文件的顺序读写、随机读写、判断文件结束和文件缓冲区详细解析【图文详解】&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 欢迎来到本篇博客&…

如何备考2025年AMC8竞赛?吃透2000-2024年600道真题(免费送题)

最近有家长朋友问我&#xff0c;现在有哪些类似于奥数的比赛可以参加&#xff1f;我的建议可以关注下AMC8的竞赛&#xff0c;类似于国内的奥数&#xff0c;但是其难度要比国内的奥数低一些&#xff0c;而且比赛门槛更低&#xff0c;考试也更方便。比赛的题目尤其是应用题比较有…

Redis开源协议变更!Garnet:微软开源代替方案?

Garnet&#xff1a;微软开源的高性能替代方案&#xff0c;秉承兼容 RESP 协议的同时&#xff0c;以卓越性能和无缝迁移能力重新定义分布式缓存存储&#xff01; - 精选真开源&#xff0c;释放新价值。 概览 最近&#xff0c;Redis修改了开源协议&#xff0c;从BSD变成了 SSPLv…

第二十一章 Jquery ajax

文章目录 1. jquery下载2. jquery的使用3. jquery页面加载完毕执行4. jquery属性控制6. 遍历器 2. ajax1. 准备后台服务器2. ajax发送get请求3. ajax发送post请求 1. jquery下载 点击下载 稳定版本1.9 2. jquery的使用 存放到html文件的同级目录 3. jquery页面加载完毕执行…

Unity | 射线检测及EventSystem总结

目录 一、知识概述 1.Input.mousePosition 2.Camera.ScreenToWorldPoint 3.Camera.ScreenPointToRay 4.Physics2D.Raycast 二、射线相关 1.3D&#xff08;包括UI&#xff09;、射线与ScreenPointToRay 2.3D&#xff08;包括UI&#xff09;、射线与ScreenToWorldPoint …

Linux安装redis(基于CentOS系统,Ubuntu也可参考)

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、下载并解压Redis 1、执行下面的命令下载redis&#xff1a;wget https://download.redis.io/releases/redis-6.2.6.tar.gz 2、解压redis&#xff1a;tar xzf redis-6.2.6.tar.gz 3、移动redis目录&a…

“直播曝光“有哪些媒体直播分流资源?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 我们线下举办活动时&#xff0c;往往希望活动进行更大的曝光&#xff0c;随着视频直播越来越被大众认可&#xff0c;甚至成了活动的标配&#xff0c;那么做活动视频直播的时候&#xff0…

admin端

一、创建项目 1.1 技术栈 1.2 vite 项目初始化 npm init vitelatest vue3-element-admin --template vue-ts 1.3 src 路径别名配置 Vite 配置 配置 vite.config.ts // https://vitejs.dev/config/import { UserConfig, ConfigEnv, loadEnv, defineConfig } from vite im…

|行业洞察·趋势报告|《2024旅游度假市场简析报告-17页》

报告的主要内容解读&#xff1a; 居民收入提高推动旅游业发展&#xff1a;报告指出&#xff0c;随着人均GDP的提升&#xff0c;居民的消费能力增强&#xff0c;旅游需求从传统的观光游向休闲、度假游转变&#xff0c;国内人均旅游消费持续增加。 政府政策促进旅游市场复苏&…

公众号的AI聊天机器人已修复!谷歌Gemini Pro 10大使用场景解析

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

WorkPlus Meet构建局域网视频会议解决方案,助力企业协同与沟通

在当今数字化时代&#xff0c;局域网视频会议扮演着企业协同与沟通的重要角色。而选择适合的局域网视频会议平台能够提升企业的协作效率与沟通效果。WorkPlus Meet以其卓越的性能和强大的功能&#xff0c;成为企业局域网视频会议的首选。 局域网视频会议的优势与作用不言而喻。…