C++20 新特性(22):C++ attribute的改进
liebian365 2024-12-27 16:16 24 浏览 0 评论
C++ attribute 简介
C++ 的 attribute 是一种标准的语法结构来对语言进行扩展,用来统一各种编译器的自定义扩展,例如 gcc 的 __attribute__((...)) ,或者微软的 __declspec() 。
C++ 语言本身定义了一些标准的 attribute ,例如 [[ noreturn ]] 、 [[ nodiscard ]] 等,也支持编译器特定的 attribute ,例如 [[ gnu::unused ]] 等,
C++20 对 [[ nodiscard ]] 的改进
在 C++17 中引入了 [[ nodiscard ]] 属性,主要是用来标记一个对象或者一个函数,通知编译器这个对象或者这个函数返回的对象不应该被丢弃,如果被丢弃了,编译器应该发出警告。
C++20 在 C++17 的基础上,对 [[ nodiscard ]] 属性进行了改进:
- 支持 [[ nodiscard ]] 属性增加参数,用于说明理由
- 对于构造函数也标记 [[ nodiscard ]] 属性
下面通过一些例子来说明 [[ nodiscard ]] 属性:
#include <iostream>
using std::cout, std::endl;
// <1> 类本身是 nodiscard ,函数返回这个类的对象时不可丢弃
struct [[ nodiscard("for example, maybe memory leak") ]] SA
{
int m_a;
};
struct SC
{
SC() : m_b( 7 ) {} // <2> 普通构造函数
[[ nodiscard ]] SC( int fd, int b ) : m_b ( b ) {} // <3> 特定的构造函数,构建的对象不可丢弃
int m_b;
};
// <21> 函数返回的对象不可丢弃
struct SA func_1()
{
return SA { 5 };
}
// <22> 返回的是对象的引用,而不是对象,不同的类型,因此不算不可丢弃
struct SA & func_2()
{
static SA a1 { 5 };
return a1;
}
// <23> 函数本身有nodiscard标记,不管返回什么对象都不可丢弃
[[ nodiscard ]] struct SC func_3()
{
return SC();
}
// <24> 普通函数,SC类也只是某些构造函数不可丢弃,不是整个类本身不可丢弃,因此返回的结果也不算不可丢弃
struct SC func_4()
{
return SC( 2, 3 );
}
int main( int argc, char * argv[] )
{
SA(); // <31> 构造不可丢弃对象,但又丢弃了,需要给出警告。(不过 gcc 10 未给出警告)
(void)SA(); // <32> 除非显式说明不关心返回值,不需要给出警告
SC(); // <33> 调用普通构造函数,不需要给出警告
SC( 3, 2 ); // <34> 调用 nodiscard 的构造函数,但又丢弃了,需要给出警告
(void)SC( 4, 3 ); // <35> 显式说明不关心返回值,不需要给出警告
struct SC a1 = SC( 5, 4 ); // <36> 构造给具体的有名字的对象,没有丢弃,不需要警告
func_1(); // <41> 函数返回不可丢弃对象,但又丢弃了,需要给出警告
struct SA a2;
a2 = func_1(); // <42> 函数返回值赋值给对象,没有丢弃,不需要给出警告
func_2(); // <43> 函数返回不可丢弃对象的引用,不是不可丢弃对象,类型不同,不需要给出警告
func_3(); // <44> 函数本身有不可丢弃标记,任何类型的返回值都不可丢弃,需要给出警告
struct SC a3 = func_3(); // <45> 返回值没有丢弃,不需要给出警告
func_4(); // <46> 普通函数,不需要给出警告
cout << a1.m_b << " " << a2.m_a << " " << a3.m_b << endl;
return 0;
}
编译和运行结果为:
[smlc@test code]$ g++ -std=c++20 a22.cpp
a22.cpp: In function ‘int main(int, char**)’:
a22.cpp:51:11: warning: ignoring return value of ‘SC::SC(int, int)’, declared with attribute ‘nodiscard’ [-Wunused-result]
51 | SC( 3, 2 ); // <34> 调用 nodiscard 的构造函数,但又丢弃了,需要给出警告
| ^
a22.cpp:17:18: note: declared here
17 | [[ nodiscard ]] SC( int fd, int b ) : m_b ( b ) {} // <3> 特定的构造函数,构建的对象不可丢弃
| ^~
a22.cpp:55:9: warning: ignoring returned value of type ‘SA’, declared with attribute ‘nodiscard’: ‘for example, maybe memory leak’ [-Wunused-result]
55 | func_1(); // <41> 函数返回不可丢弃对象,但又丢弃了,需要给出警告
| ^
a22.cpp:22:11: note: in call to ‘SA func_1()’, declared here
22 | struct SA func_1()
| ^~~~~~
a22.cpp:9:58: note: ‘SA’ declared here
9 | struct [[ nodiscard("for example, maybe memory leak") ]] SA
| ^~
a22.cpp:59:9: warning: ignoring return value of ‘SC func_3()’, declared with attribute ‘nodiscard’ [-Wunused-result]
59 | func_3(); // <44> 函数本身有不可丢弃标记,任何类型的返回值都不可丢弃,需要给出警告
| ^
a22.cpp:35:27: note: declared here
35 | [[ nodiscard ]] struct SC func_3()
| ^~~~~~
[smlc@test code]$ ./a.out
4 5 7
新增 likely 和 unlikely 属性
C++20 新增了likely 和 unlikely 属性,可以应用在 case 语句中,以及非定义变量的其他普通语句(statement)中,用来说明这个语句所在的代码执行路径,在运行时被执行的可能性会更高或更低,以便编译器可以有针对性的进行优化,达到命中率更高的执行路径分支预测,提高执行效率。
#include <iostream>
using std::cout, std::endl;
void func_1( int n )
{
switch( n )
{
[[ unlikely ]] case 1 : // <1> 通知编译器此 case 语句执行概率较低
cout << "1" << endl;
break;
case 2 :
cout << "2" << endl;
break;
[[ likely ]] case 5 :
[[ unlikely ]] cout << "5" << endl;
[[ likely ]] cout << "5 again" << endl;
// <2> 上面的同一个路径下不同语句分别有 likely 和 unlikely ,语法上允许,语义上标准未明确此路径以哪个为准,实际代码不应该出现这样的矛盾
break;
default :
// <3> 同一个语句不应该同时有 likely 和 unlikely ,unlikely会被忽略,编译器会给出警告
[[ likely ]] [[ unlikely ]] cout << "99 : ( " << n << " )" << endl;
break;
}
if( n > 3 ) [[ likely ]] { // <4> if 的这个分支概率更高,可以将 likely 放在这个位置
[[ likely ]] ; // <5> 也可以将 likely 放在路径里面,可以对空语句进行修饰
cout << "> 3" << endl;
}
else {
[[ unlikly ]] int a = 3; // <6> 不支持对定义变量的语句进行修饰,likely 或 unlikely 会被忽略,编译器会给出警告
cout << "not > " << a << endl;
}
return;
}
int main( int argc, char * argv[] )
{
func_1( 5 );
return 0;
}
用于测试是否支持某个属性的宏:__has_cpp_attribute
为了便于测试是否支持某个属性,C++20 中新增了一个宏 __has_cpp_attribute,可以检查某个属性是否支持,支持到哪个版本,这个宏的值是一个表示年月的整数。例如 nodiscard 属性的测试宏 __has_cpp_attribute( nodiscard ) 对于C++17(不支持reason参数)取值是201603,对于C++20(支持reason参数)取值是201907。应用程序可以根据宏的取值来判断支持程度,编写相应的代码。
除了测试是否支持某个属性外,C++20还定义了很多宏,用于判断是否支持某个语言特性,或者是否支持某个基础库,以便应用程序根据编译器和基础库的不同能力,执行不同的代码。
下面是一些例子:
#include <iostream>
using std::cout, std::endl;
// <1> 首先检查是否支持检测属性的宏
#ifdef __has_cpp_attribute
// <2> 然后根据属性的具体的值,来判断支持到什么程度
// 例如 nodiscard 的值是 201907,表示支持C++20中增强的带 reason 参数的 nodiscard 属性
#if __has_cpp_attribute( nodiscard ) >= 201907
[[ nodiscard( "should not discard this int" ) ]] int func_1()
{
cout << "nodiscard : " << __has_cpp_attribute( nodiscard ) << endl;
return 5;
}
// <3> 例如 nodiscard 的值是 201603,表示支持C++17中不带 reason 参数的 nodiscard 属性
#elif __has_cpp_attribute( nodiscard ) >= 201603
[[ nodiscard ]] int func_1()
{
cout << "nodiscard : " << __has_cpp_attribute( nodiscard ) << endl;
return 5;
}
#else
// <4> 支持 __has_cpp_attribute 宏,但不支持 nodiscard 属性,(实际不应该出现,nodiscard 比 __has_cpp_attribute 还要早)
int func_1()
{
cout << "nodiscard : " << __has_cpp_attribute( nodiscard ) << endl;
return 5;
}
#endif
#else
// <5> 不支持 __has_cpp_attribute 宏
int func_1()
{
// cout << "nodiscard : " << __has_cpp_attribute( nodiscard ) << endl;
return 5;
}
#endif
// <6> 先判断是否支持检查头文件,再判断某个头文件是否存在,存在了再包含此头文件
#ifdef __has_include
#if __has_include( <chrono> )
#include <chrono>
#endif
#endif
int main( int argc, char * argv[] )
{
func_1();
// <7> 对于不支持的属性,__has_cpp_attribute() 宏返回值是 0
cout << "__has_cpp_attribute( nonexist ) : " << __has_cpp_attribute( nonexist ) << endl;
// <8> 检查语言特性的宏,是否支持 countexpr 语句
#ifdef __cpp_constexpr
cout << "__cpp_constexpr : " << __cpp_constexpr << endl;
#else
cout << "__cpp_constexpr : (undefined)" << endl;
#endif
// <9> 检查基础库特性的宏,是否包含了 <chrono> 基础库
#ifdef __cpp_lib_chrono
cout << "__cpp_lib_chrono : " << __cpp_lib_chrono << endl;
#else
cout << "__cpp_lib_chrono : (undefined)" << endl;
#endif
return 0;
}
【往期回顾】
相关推荐
- “版本末期”了?下周平衡补丁!国服最强5套牌!上分首选
-
明天,酒馆战棋就将迎来大更新,也聊了很多天战棋相关的内容了,趁此机会,给兄弟们穿插一篇构筑模式的卡组推荐!老规矩,我们先来看10职业胜率。目前10职业胜率排名与一周前基本类似,没有太多的变化。平衡补丁...
- VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"
-
首先,程序中头文件的选择,要选择头文件,在文件中是没有对M_PI的定义的。选择:项目——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,...
- 东营交警实名曝光一批酒驾人员名单 88人受处罚
-
齐鲁网·闪电新闻5月24日讯酒后驾驶是对自己和他人生命安全极不负责的行为,为守护大家的平安出行路,东营交警一直将酒驾作为重点打击对象。5月23日,东营交警公布最新一批饮酒、醉酒名单。对以下驾驶人醉酒...
- Qt界面——搭配QCustomPlot(qt platform)
-
这是我第一个使用QCustomPlot控件的上位机,通过串口精确的5ms发送一次数据,再将读取的数据绘制到图表中。界面方面,尝试卡片式设计,外加QSS简单的配了个色。QCustomPlot官网:Qt...
- 大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写
-
老友相聚,仗剑江湖!《大话西游2》2021全民PK季4月激燃打响,各PK玩法鏖战齐开,零门槛参与热情高涨。PK季期间,不仅各种玩法奖励丰厚,参与PK趣闻录活动,投稿自己在PK季遇到的趣事,还有机会带走...
- 测试谷歌VS Code AI 编程插件 Gemini Code Assist
-
用ClaudeSonnet3.7的天气测试编码,让谷歌VSCodeAI编程插件GeminiCodeAssist自动编程。生成的文件在浏览器中的效果如下:(附源代码)VSCode...
- 顾爷想知道第4.5期 国服便利性到底需优化啥?
-
前段时间DNF国服推出了名为“阿拉德B计划”的系列改版计划,截至目前我们已经看到了两项实装。不过关于便利性上,国服似乎还有很多路要走。自从顾爷回归DNF以来,几乎每天都在跟我抱怨关于DNF里面各种各样...
- 掌握Visual Studio项目配置【基础篇】
-
1.前言VisualStudio是Windows上最常用的C++集成开发环境之一,简称VS。VS功能十分强大,对应的,其配置系统较为复杂。不管是对于初学者还是有一定开发经验的开发者来说,捋清楚VS...
- 还嫌LED驱动设计套路深?那就来看看这篇文章吧
-
随着LED在各个领域的不同应用需求,LED驱动电路也在不断进步和发展。本文从LED的特性入手,推导出适合LED的电源驱动类型,再进一步介绍各类LED驱动设计。设计必读:LED四个关键特性特性一:非线...
- Visual Studio Community 2022(VS2022)安装图文方法
-
直接上步骤:1,首先可以下载安装一个VisualStudio安装器,叫做VisualStudioinstaller。这个安装文件很小,很快就安装完成了。2,打开VisualStudioins...
- Qt添加MSVC构建套件的方法(qt添加c++11)
-
前言有些时候,在Windows下因为某些需求需要使用MSVC编译器对程序进行编译,假设我们安装Qt的时候又只是安装了MingW构建套件,那么此时我们该如何给现有的Qt添加一个MSVC构建套件呢?本文以...
- Qt为什么站稳c++GUI的top1(qt c)
-
为什么现在QT越来越成为c++界面编程的第一选择,从事QT编程多年,在这之前做C++界面都是基于MFC。当时为什么会从MFC转到QT?主要原因是MFC开发界面想做得好看一些十分困难,引用第三方基于MF...
- qt开发IDE应该选择VS还是qt creator
-
如果一个公司选择了qt来开发自己的产品,在面临IDE的选择时会出现vs或者qtcreator,选择qt的IDE需要结合产品需求、部署平台、项目定位、程序猿本身和公司战略,因为大的软件产品需要明确IDE...
- Qt 5.14.2超详细安装教程,不会来打我
-
Qt简介Qt(官方发音[kju:t],音同cute)是一个跨平台的C++开库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序。Qt是纯C++开...
- Cygwin配置与使用(四)——VI字体和颜色的配置
-
简介:VI的操作模式,基本上VI可以分为三种状态,分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode),各模式的功能区分如下:1)...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- “版本末期”了?下周平衡补丁!国服最强5套牌!上分首选
- VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"
- 东营交警实名曝光一批酒驾人员名单 88人受处罚
- Qt界面——搭配QCustomPlot(qt platform)
- 大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写
- 测试谷歌VS Code AI 编程插件 Gemini Code Assist
- 顾爷想知道第4.5期 国服便利性到底需优化啥?
- 掌握Visual Studio项目配置【基础篇】
- 还嫌LED驱动设计套路深?那就来看看这篇文章吧
- Visual Studio Community 2022(VS2022)安装图文方法
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)