跳转至

C++属性

- gcc 和 clang 编译器不仅支持`__attribute__ GNU扩展属性,各种编译器又独自支持的扩展属性。
- GCC 和 Clang 支持把相当一部分 __attribute__ 表示的属性改写成 [[]] ,但 MSVC 几乎不支持把 __declspec 改写成 [[]] 。
- `__attribute__ ` :是 GNU 扩展的属性语法元素,有些编译器可能会把它当成普通的标识符,产生各种意想不到的错误。gcc 和 clang 编译器支持。
- ` __declspec` : windows 平台 MSVC 编译器对属性的扩展。
- `[[属性]]` : C++11 中加入了标准化的属性写法 [[]] ,语法上大致可以出现在上面的扩展属性能出现的地方。 C++17 开始标准要求忽略不支持的属性。 C2x 在最近引入了 [[]] 和一些 C++ 的标准属性。
  • 参考
  • 功能:为类型、对象、代码等引入由实现定义的属性,这些属性扮演这限制或说明被修饰物的作用。属性为各种由实现定义的语言扩展(例如 GNU 与 IBM 的语言扩展__attribute__((...)),微软的语言扩展__declspec() 等)提供了统一化的语法
  • 语法:
  • [[ 属性列表 ]] (C++11 起)
  • [[ using 属性命名空间 : 属性列表 ]] (C++17 起)
  • 语法示例
  • 1) 简单属性,例如 [[noreturn]]
  • 2) 有命名空间的属性,例如 [[gnu::unused]]
  • 3) 有实参的属性,例如 [[deprecated("because")]]
  • 4) 既有命名空间又有实参列表的属性
  • 规则
  • 规则1:若using: namespace出现在属性列表开端,则属性列表中的其他属性均不得指定命名空间:由using所指定的命名空间会应用到所有这些属性
  • 规则2:所有实现所未知的属性均被忽略,且不产生错误。 (C++17 起)
  • 示例
// 规则2:以下属性不是标准中定义的,且未知,将忽略属性声明
[[using CC: opt(1), debug]] // 同 [[CC::opt(1), CC::debug]]
// 规则1:不遵循规则1
[[using CC: CC::opt(1)]] // 错误:使用前缀的属性与作用域属性标记一起使用

1 现有标准属性列表

attribute 功能
[[noreturn]](C++11) 指示函数不返回
[[carries_dependency]](C++11) 指示释放消费 std::memory_order 中的依赖链传入和传出该函数。
[[deprecated]](C++14)、[[deprecated("原因")]](C++14) 指示允许使用声明有此属性的名称或实体,但因 原因 而不鼓励使用。
[[fallthrough]](C++17) 指示从前一 case 标号直落是有意的,而在发生直落时给出警告的编译器不应该为此诊断。
[[nodiscard]](C++17)、[[nodiscard("原因")]](C++20) 鼓励编译器在返回值被舍弃时发布警告。
[[maybe_unused]](C++17) 压制编译器在未使用实体上的警告,若存在。
[[likely]](C++20)
[[unlikely]](C++20) 指示编译器应该针对通过某语句的执行路径比任何其他执行路径更可能或更不可能的情况进行优化。
[[no_unique_address]](C++20) 指示非静态数据成员不需要拥有不同于其类的所有其他非静态数据成员的地址。
[[optimize_for_synchronized]](TM TS) 指示应该针对来自 synchronized 语句的调用来优化该函数定义

2 C++11

2.1 noreturn

  • 功能:指示函数不返回。
  • 规则:
  • 规则1:此属性仅应用到函数声明中正在声明的函数名。若拥有此属性的函数实际上返回,则行为未定义。
  • 规则 2:若函数的任何声明指定此属性,则其首个声明必须指定它。若函数在一个翻译单元中声明为带 [[noreturn]] 属性,而同一函数在另一翻译单元中声明为不带 [[noreturn]] 属性,则程序非良构;不要求诊断。
  • 示例
[[ noreturn ]] void f() {
  throw "error";
  // OK
}
void q [[ noreturn ]] (int i) {
  // 若以 <= 0 的参数调用则行为未定义
  if (i > 0) {
    throw "positive";
  }
}
// void h() [[noreturn]]; // 错误:属性应用到 h 的函数类型,而非 h 自身

3 C++14

4 C++17

5 C++20

5.1 no_unique_address

  • 功能:允许此成员与其类的其他非静态数据成员或基类子对象重叠。这表示若该成员拥有空类型(例如无状态分配器),则编译器可将它优化为不占空间,正如同假如它是空基类一样。若该成员非空,则其中的任何尾随填充空间亦可复用于存储其他数据成员。
  • 优点:压缩类大小,降低内存占用
  • 规则
  • 规则1:类中必须有一个字节的大小
  • 规则2:相同类型的空类对象不能共享同一地址
#include<iostream>
struct Empty{
    void print(){std::cout<<"hello"<<std::endl;}
};
struct Empty2{
    void print(){std::cout<<"hello"<<std::endl;}
};
struct A{
    int i;
    Empty t;
};
struct B{
    int i;
    [[no_unique_address]] Empty t;
};
struct C{
    [[no_unique_address]] Empty t;
};
struct D{
    char c;
    [[no_unique_address]] Empty t1,t2;
};
struct E{
    char c[2];
    [[no_unique_address]] Empty t1,t2;
};
struct F{
    char c;
    [[no_unique_address]] Empty t1;
    [[no_unique_address]] Empty2 t3;
};
int main() {
   using namespace std;
   cout<<"sizeof(Empty):"<<sizeof(Empty)    /*空类大小为1*/
   <<" sizeof(A):"<<sizeof(A)               /*4+1+3(内存对齐)==8*/
   <<" sizeof(B):"<<sizeof(B)               /*no_unique_address声明的t与i共享内存*/
   <<" sizeof(C):"<<sizeof(C)               /*规则1:不遵循规则1,不能共享,类大小==1*/
   <<" sizeof(D):"<<sizeof(D)               /*规则2:不遵循规则2,t1、t2相同类型,不能共享同一地址*/
   <<" sizeof(E):"<<sizeof(E)               /*规则2:t1与c[0]共享地址,t2与c[1]共享地址,但测试发现编译器,没有优化共享,理论应该==2,但实际==3*/
   <<" sizeof(F):"<<sizeof(F)<<endl;        /*规则2:遵循规则2*/
}
// 输出:sizeof(Empty):1 sizeof(A):8 sizeof(B):4 sizeof(C):1 sizeof(D):2 sizeof(E):3 sizeof(F):1

5.2 likely、unlikely

  • 功能:指示编译器对更可能发生(likely)和更不可能发生(unlikely)进行优化。
  • 示例
#include <iostream>
int f1(int i)
{
    if (i < 0) [[unlikely]] {
        return 0;
    }
    return 1;
}
int f2(int i)
{
    switch(i) {
    case 1: [[fallthrough]];
    [[likely]] case 2: return 1;
    }
    return 2;
}
int main()
{
    std::cout << f1(-1) << std::endl;
    std::cout << f2(1) << std::endl;
}