C/C++编程:属性(attr)、说明符

属性

作用

  • 为类型、对象、代码等引入由实现定义的属性
  • 属性为各种由实现定义的语言扩展(例如 GNU 与 IBM 的语言扩展 attribute((…)),微软的语言扩展 __declspec() 等)提供了统一化的语法。
  • 属性可用在 C++ 程序中的几乎所有位置,而且可应用于几乎所有事物:类型、变量、函数、名字、代码块、整个翻译单元,不过每个特定的属性都仅在实现所容许之处有效:
    • [[expect_true]] 可能是只能与 if,而非与类声明一同使用的属性
    • [[omp::parallel()]] 可能是应用到代码块或 for 循环,而非到类型 int 等的属性。
  • 官方文档

语法

[[attr]] [[attr1, attr2, attr3(args)]] [[namespace::attr(args)]] alignas-说明符 (1)
[[ 属性列表 ]] (2)
[[ using 属性命名空间 : 属性列表 ]] (3)

(1) 例子

[[gnu::always_inline]] [[gnu::hot]] [[gnu::const]] [[nodiscard]]
inline int f1(); // 声明 f 带四个属性

(2) 例子

[[gnu::always_inline, gnu::const, gnu::hot, nodiscard]]
int f2(); // 同上,但使用含有四个属性的单个属性说明符

(3) 例子:

[[using gnu : const, always_inline, hot]] [[nodiscard]]
int f[[gnu::always_inline]](); // 属性可出现于多个说明符中

说明符

说明符包括如下:

  • typedef说明符。如果存在,则整个声明是typedef声明,且每个声明符引入一个新的类型名,而非对象或函数
  • 函数说明符(inline , virtual, explicit),仅在函数声明中允许使用。 自C++17起,inline亦允许在变量声明上使用
  • friend说明符允许在类和函数声明中使用
  • C++11起,constexpr 说明符,只允许在变量定义,函数及函数模板声明,以及具有字面类型的静态数据成员的声明中使用。
  • C++20起,consteval 说明符,只允许在函数和函数模板声明中使用。
  • C++20起,constinit 说明符,只允许在拥有静态或线程存储期的变量声明中使用。一个 声明说明符序列 中至多允许出现 constexpr 、 consteval 和 constinit 说明符中的一者。
  • 存储类说明符(register、static、thread_local (C++11 起)、extern、mutable)。仅允许使用一个存储类说明符,但 thread_local 可以与 extern 或 static 一同出现。
  • 类型说明符(也叫做类型说明符序列)。类型说明符是以空白分隔的说明符(仅能包含下列说明符)序列
    • 类说明符:class、struct、union
    • 枚举说明符:enum
    • 简单类型说明符
      • char、char8_t (C++20 起)、char16_t、char32_t (C++11 起)、wchar_t、bool、short、int、long、signed、unsigned、float、double、void
      • auto、decltype 说明符
      • 先前声明过的类名、枚举名、typedef名、(C++11起)类型别名
      • 带模板实参的模板名、无模板实参的模板名
    • 详细类型说明符
      • 关键词 class、struct 或 union,后随先前已定义为 class、struct 或 union 名的标识符
      • 关键词 class、struct 或 union,后随带模板实参的、先前已定义为类模板名字的模板名
      • 关键词 enum 后随先前已定义为枚举名的标识符
    • typename说明符
    • cv限定符说明符

声明说明符序列

  • 声明说明符序列(简称声明说明符)是以空白分隔的说明符序列,顺序任意
  • 每个声明说明符序列 中只允许一个类型说明符,但有以下例外:
    • const 、volatile 能与自身外的任何类型说明符组合
    • signedunsigned能与 char、long、short 或 int 组合。
    • shortlong 能与 int 组合
    • long 能与 long 组合。(C++11 起)
      • 声明说明符序列 中仅有的允许两次出现的说明符是 long(可出现两次)。 所有其他重复,例如 const static const 或 virtual inline virtual 均为错误 (C++17 起)。
  • 属性可以出现在声明说明符中,这种情况下,它们应用于其之前的说明符所确定的类型。

声明符

声明说明符序列和声明符是两个不同的东西,看例子

class C {
    
    
    std::string member; // 声明说明符序列 为 "std::string"
                        // 声明符 为 "member"
} obj, *pObj(&obj);
// 声明说明符序列 为 "class C { std::string member; }"
// 声明符 "obj" 定义 C 类型的对象
// 声明符 "*pObj(&obj)" 声明并初始化指向 C 的指针
 
int a = 1, *p = nullptr, f(), (*pf)(double);
// 声明说明符序列 为 int
// 声明符 a=1 定义并初始化 int 类型的变量
// 声明符 *p=NULL 定义并初始化 int* 类型的变量
// 声明符 (f)() 声明(但不定义)不接收参数并返回 int 的函数
// 声明符 (*pf)(double) 定义指向接收 double 并返回 int 的函数的指针
 
int (*(*foo)(double))[3] = nullptr;
// decl-specifier-seq 为 int
// 1. 声明符 "(*(*foo)(double))[3]" 是数组声明符;
//    所声明类型是“int 的 3 元素数组 /嵌套声明符/ ”
// 2. 嵌套声明符是 "(*(*foo)(double))",它是指针声明符
//    所声明类型是“int 的 3 元素数组 的指针 /嵌套声明符/”
// 3. 嵌套声明符是 "(*foo)(double)",它是函数声明符
//    所声明类型是“以 int 的 3 元素数组 的指针 为返回值的接受一个 double 参数的函数 /嵌套声明符/”
// 4. 嵌套声明符是 "(*foo)",它是(带括号,如函数声明符语法所要求的)指针声明符。
//    所声明类型是“以 int 的 3 元素数组 的指针 为返回值的接受一个 double 参数的函数 的指针 /嵌套声明符/”
// 5. 嵌套声明符是 "foo",它是标识符。
// 该声明声明了对象 foo,其类型为“以 int 的 3 元素数组 的指针 为返回值的接受一个 double 参数的函数 的指针”
// 初始化器 "= nullptr" 提供此指针的初值。

例子

其他

有限定是在无限定的标识表达式前面带上作用域解析运算符::

猜你喜欢

转载自blog.csdn.net/zhizhengguan/article/details/114985589