C++0x 通用属性

1.编译器扩展属性

C++在不断的发展,但每一阶段的C++标准提供的功能都很难完全满足现实需求,于是为了弥补标准的不足或者扩增特性应用场景所需的特性,各大C++编译器厂商多多少少在标准之外都增加了不少有用的扩展功能。这些扩展功能并不在C++的标准中,但是却经常被使用。有时候,C++标准委员会也会考虑这些标准之外的扩增特性,将其纳入标准之中。

C++扩增特性中较为常见的就是“属性”(attribute)。属性是对语言中实体对象(比如函数、变量、类型等)附加说明,用来语言及非语言层面的功能,或是帮助编译器优化代码。不同编译器有不同的属性语法,比如对于GNU ,属性是通过关键字__attribute__来声明的,常见的有format、noreturn、const和aligned等,申明语法如下:

__attribute__ ((attribute-list))

例如:

extern int area(int n) __attribute__ ((const))

int main()
{
    int areas=0;
    for(int i=0;i<10;++i)
    {
        areas+=area(3)+i;
    }
}

程序中const属性告诉编译器,该函数返回值只依赖于输入,不会改变函数外的数据,因此编译器可以对area(3)进行优化处理,只对函数调用一次,后续将area(3)视为常量进行操作,将大大提醒程序性能。

Windows平台VC++则使用__declspec来申明扩展属性,使用语法如下:

__declspec (extended-decl-modifier)

比如控制类型对齐方式:

__declspec (align(32)) struct Struct32
{
    char c;
    int i;
} 

其作用是类型Struct32定义的变量的其实地址是32的整数倍,且类型大小sizeof(Struct32)=32也是32的整数倍。

2.C++11通用属性

2.1语法格式

自C++11开始,C++拥有统一形式的通用属性申明方式,语法格式如下:

[[attribute-list]]

语法上,C++11通用属性可以作用于函数、类型、变量、代码块等,书写位置可以位于目标之前,也可以位于目标之后。对于作用于整个语句的通用属性,则应该写在语句起始处。如果出现在以上两种位置之外的通用属性,作用于哪个实体跟编译器具体实现有关。例如:

//作用于函数
[[ attr1 ]] void func();
//或者
void func [[ attr2 ]] ();

//作用于数组
[[ attr1 ]] int array[10];
//或者
int array [[ attr1 ]] [10];

C++11只定义了两种通用属性,分别是[[ noreturn ]]与[[ carries_dependency ]],其它如[[ final ]]、[[ override ]]、[[ restrict ]]、[[ hides ]]、[[ base_check ]]等属性,考虑到通用性和实现方式,未纳入标准,比如final、override、restrict等为语言特性,通过关键字来实现。

2.2[[ noreturn ]]

[[ noreturn ]]用于标识不会返回的函数。不会返回的函数指的是被调处后面的代码不会执行,被调函数不会将控制流返回给主调函数,注意不是没有返回值的函数。通过这个属性,开发者可以告诉编译器进行代码优化,诸如死代码告警与消除等。参考如下示例:

void cout1() { cout << "cout1" << endl; }
void cout2() { cout << "cout2" << endl; }

[[noreturn]] void throwAway()
{
    throw "exception";
}

void foo()
{
    cout1();
    throwAway();
    cout2();        //该函数不可达
}

上面的代码中,cout2()不可达,编译器可以采用告警的方式提示开发者或者直接不生成调用cout2()的代码进行优化。

除了抛出异常可能会导致程序控制流不能返回调用者外,其它诸如包含终止应用程序或者无限循环语句的函数,都可以使用[[noreturn]]进行申明,比如C++11标准库中,我们可以看到如下函数申明语句:

[[noreturn]] void abort(void) noexcept;

当然,[[noreturn]]可以帮助编译器进行代码优化,前提是正确使用。如果错误地使用[[noreturn]]可能会给程序带来致命损害,因此要小心翼翼。

2.3[[carries_dependency]]

[[ carries_dependency ]]作用于函数参数与返回值,用于避免在弱一致性模型平台上产生不必要的内存栅栏,降低代码效率。比如:

atomic<int*> a;
...
int* p=(int*)a.reload(memory_order_consume);
func(p);

由于编译器在编译到func时不知道func中的具体实现,因为使用了原子变量a对p赋值时使用了memory_order_consume内存顺序模型,所以需要保证a.load先于任何关于a(或者p)的操作,编译其往往会在func函数之前加入一条内存栅栏。然而如果func的实现是:

void func(int* p)
{
    //...假设p2是一个atomic<int*>的变量
    p2.store(p,memory_order_release);
}

那么对于func函数来说,由于使用memory_order_release的内存顺序,p2.store对p的使用会被保证在任何关于p的操作之后进行。这样一来,编译器在func函数之前加入内存栅栏毫无意义,并影响了性能,解决办法是对函数参数使用[[ carries_dependency ]]属性:

void func(int* [[ carries_dependency ]] p);

同样的,[[ carries_dependency ]]也可以用于返回值,语法格式如下:

[[ carries_dependency ]] int* func();

事实上,对于强内存模型平台来说,如x86-64,编译器往往会忽略该属性,因此该属性使用比较有限。如果开发平台是弱类型模型,并且很关心并行程序的执行性能时,可以考虑使用[[ carries_dependency ]]。

3.C++14与C++17通用属性

在C++11的基础上,C++新标准C++14与C++17对通用属性进行了补充,主要有:

(1)[[deprecated]]与[[deprecated(“reason”)]] 标准:C++14; 作用:指示允许使用声明有此属性的名称或实体,但因reason不鼓励使用; 示例:class [[deprecated]] Outdate{};

(2)[[fallthrough]] 标准:C++17; 作用:出现在switch语句中,抑制上一句case没有break而引起的fallthrough的警告; 示例:

switch(i) 
{
    case 1: something();[[fallthrough]];
case 2:something();[[fallthrough]];
default:break;
}

(3)[[nodiscard]] 标准:C++17; 作用:若返回值被舍弃,则鼓励编译器发出警告。 示例:

[[nodiscard]] int something()
{
    return 1;
}

(4)[[maybe_unused]] 标准:C++17; 作用:抑制编译器在未使用实体上发出警告。 示例:

 [[maybe_unused]] int a = something();

使用以上通用属性,可以帮助我们更好的优化和管理代码。C++20已经在路上,新标准会继续在通用属性方面作出更多的扩增。


参考文献

[1]深入理解C++11[M].8.2通用属性 [2]《深入理解C++11》笔记-对齐支持和通用属性 [3]attribute specifier sequence(since C++11) [4]C++的属性指示符

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏王亚昌的专栏

C++多线程编程学习二 [类中封装互斥量的设计]

      之前我也提到过,如果一个类的数据成员中在多线程环境中可能会被竞争使用时,一定要在类中解决这个问题,而不是在代码编写过程中在每次使用时去申请或释放,这...

9410
来自专栏Crossin的编程教室

这些年,你们一起踩过的坑(2)

上次我们踩坑总结文章 这些年,你们一起踩过的坑(1) 受到了不少同学的认可。我也确信文中所涉及的问题是非常具有普遍性的,对绝大多数初学者都会有帮助。

12130
来自专栏java一日一条

谈谈 Hash Table

结构体(或对象)可以是基本数据类型或者其他结构体(或对象)的组合。结构体或对象一般用来描述一个复杂数据实体。

6320
来自专栏机器学习算法与Python学习

Python中的实用小技巧

关键字全网搜索最新排名 【机器学习算法】:排名第一 【机器学习】:排名第二 【Python】:排名第三 【算法】:排名第四 话说python是一个大杂会,既可以...

34850
来自专栏tkokof 的技术,小趣及杂念

位运算实用指南

说明 : 想来这应该是初次接触移位操作符时一定会了解到的知识点,根据2进制的整数表示方法应该不难理解,原因细节不再赘述~

7110
来自专栏数据小魔方

R语言学习笔记——R语言面向对象编程系列2

最近在看任坤大神的新作——《R语言编程指南》,其中对于编程语言中非常流行的面向对象编程范式(OOP)在R语言中的实现进行了非常详尽的讲解,强烈推荐各位有志于进阶...

357120
来自专栏青玉伏案

代码重构(四):条件表达式重构规则

继续更新有关重构的博客,前三篇是关于类、函数和数据的重构的博客,内容还算比较充实吧。今天继续更新,本篇博客的主题是关于条件表达式的重构规则。有时候在实现比较复杂...

20890
来自专栏书山有路勤为径

分治算法之归并排序

分治算法: 将一个规模为N的问题分解为K个规模较小的子问题,这些子问题互相独立且与原问题性质相同。求出子问题的解后进行合并,就可得到原问题的解。

8510
来自专栏编程

手把手教你半个小时用python语言编程出你的第一个程序

学习目标 知道有序的软件开发过程的步骤。 了解遵循输入、处理、输出(IPO)模式的程序,并能够以简单的方式修改它们。 了解构成有效Python标识符和表达式的规...

42250
来自专栏算法修养

接口和多态性

如果你又加了一个百度外卖,那么eat函数中又要new 一个BaiDu() ,给开发带来麻烦。我们希望的是,如果代码要扩展了,那么代码要尽最大可能的进行很小的改动...

9530

扫码关注云+社区

领取腾讯云代金券