属性规范序列:现代C++的编译指示

随着C++语言的发展,C++11 标准引入了属性规范序列(Attributes),为开发者提供了一种新的编译指示方法。属性规范通过在代码中嵌入特定的指令,帮助编译器优化代码,提升性能,同时提高代码的可读性和安全性。这一特性不仅增强了编程的灵活性,还使得编写高效、易维护的代码变得更加容易。

属性规范序列使用双方括号 [[...]] 包裹,这种语法使得属性指示在代码中显得简洁且不干扰代码的主要逻辑。通过属性规范,开发者可以向编译器传达更多信息,例如建议优化行为、标记废弃的功能或提示特定的代码约束等。

语法

从 C++11 开始,属性可以通过以下语法使用:

c++ 复制代码
[[ attribute-list ]]

从 C++17 开始,还可以指定属性的命名空间:

c++ 复制代码
[[ using attribute-namespace : attribute-list ]]

其中 attribute-list 是由零个或多个属性构成的逗号分隔序列,可能以省略号(...)结束,表示一个包扩展。

属性可以是以下形式:

  1. 简单属性,例如 [[noreturn]]
  2. 带有命名空间的属性,例如 [[gnu::unused]]
  3. 带有参数的属性,例如 [[deprecated("因某原因")]]
  4. 同时带有命名空间和参数列表的属性。

如果在属性列表的开始使用 using namespace:,则列表中的所有其他属性都应用这个命名空间,不需要再次指定:

c++ 复制代码
[[using CC: opt(1), debug]] // 等同于 [[CC::opt(1), CC::debug]]
[[using CC: CC::opt(1)]] // 错误:不能结合使用 using 和带作用域的属性

属性提供了一种统一的标准语法,用于实现定义的语言扩展,例如 GNU 和 IBM 的语言扩展 __attribute__((...)),Microsoft 的扩展 __declspec() 等。属性几乎可以用在 C++ 程序的任何地方,适用于几乎所有东西:类型、变量、函数、名称、代码块、整个翻译单元。然而,每个特定的属性只有在被实现允许的地方才有效,例如 [[expect_true]] 可能是一个只能用于 if 语句的属性,而不能用于类声明。

在声明中,属性可以出现在整个声明之前或直接在被声明实体的名称之后,在后一种情况下,属性会被组合使用。在大多数其他情况下,属性适用于直接前面的实体。

C++属性规范一览

属性 C++标准版本 用途
[[noreturn]] C++11 表示函数不返回。
[[carries_dependency]] C++11 表示在 release-consume 内存顺序中,依赖链在函数内外传播。
[[deprecated]] C++14 表示允许但不建议使用该属性声明的名称或实体。
[[deprecated("reason")]] C++14 表示允许但不建议使用该属性声明的名称或实体,并提供弃用原因。
[[fallthrough]] C++17 表示从前一个case标签落入的情况是故意的,不应被编译器警告。
[[maybe_unused]] C++17 抑制未使用实体的编译器警告。
[[nodiscard]] C++17 鼓励编译器在返回值被丢弃时发出警告。
[[nodiscard("reason")]] C++20 鼓励编译器在返回值被丢弃时发出警告,并提供原因。
[[likely]] C++20 表示编译器应该优化某条执行路径,比其他路径更可能执行。
[[unlikely]] C++20 表示编译器应该优化某条执行路径,比其他路径更不可能执行。
[[no_unique_address]] C++20 表示非静态数据成员不需要具有与类的所有其他非静态数据成员不同的地址。
[[assume(expression)]] C++23 指定在给定点表达式总是评估为true。
[[indeterminate]] C++26 指定如果对象未初始化,则其值是不确定的。

以下是每种语法形式的示例,每种示例都展示了一个常用属性:

简单属性

c++ 复制代码
[[noreturn]] void fatalError(const char* msg) {
    std::cerr << msg << std::endl;
    std::abort();
}

在这个示例中,[[noreturn]] 表示 fatalError 函数不会返回。

带有命名空间的属性

c++ 复制代码
[[gnu::unused]] void foo(int x) {
    // 此函数的参数 x 被标记为未使用
}

在这个示例中,[[gnu::unused]]是一个 GNU 扩展的属性,用于指示编译器一个变量或函数可能不会被使用,但这种未使用的情况不应该触发编译器警告。 属性来自 gnu 命名空间,表示参数 x 可能未使用。

带有参数的属性

c++ 复制代码
[[deprecated("使用 newFunction 代替")]] void oldFunction() {
    // 这个函数已过时
}

在这个示例中,[[deprecated("使用 newFunction 代替")]] 表示 oldFunction 函数已过时,并建议使用 newFunction

同时带有命名空间和参数列表的属性

c++ 复制代码
[[gnu::always_inline]] inline void bar() {
    // 此函数总是内联
}

在这个示例中,[[gnu::always_inline]] 用于告诉编译器尽可能将某个函数内联,无论其它的优化设置如何。这是 GNU 扩展中定义的一个非标准属性。

使用 using namespace: 的属性

c++ 复制代码
[[using CC: opt(1), debug]] void optimizedFunction() {
    // 此函数带有 CC 命名空间的 opt 和 debug 属性
}

在这个示例中,[[using CC: opt(1), debug]] 表示所有属性都使用 CC 命名空间。

属性在声明中的位置

在整个声明之前

c++ 复制代码
[[nodiscard]] int calculate() {
    return 42;
}

在这个示例中,[[nodiscard]] 表示返回值不应被忽略。

在声明实体的名称之后

c++ 复制代码
int [[nodiscard]] calculate() {
    return 42;
}

在这个示例中,[[nodiscard]] 放在函数名称之后,具有相同的效果。

使用属性在代码块中

c++ 复制代码
void process() {
    if (condition) {
        [[likely]] doSomething();
    } else {
        [[unlikely]] handleFailure();
    }
}

在这个示例中,[[likely]][[unlikely]] 用于提示编译器优化不同的代码路径。

这些示例展示了如何在各种语法环境中使用属性,以便于提高代码的可读性和优化编译过程。

属性规范的扩展使用

属性规范不仅可以应用于函数和变量,还可以用于其他代码元素,如类和模板参数。通过合理使用属性规范,可以大大提高代码的自文档性和维护性。

相关推荐
敲敲了个代码7 小时前
从硬编码到 Schema 推断:前端表单开发的工程化转型
前端·javascript·vue.js·学习·面试·职场和发展·前端框架
dly_blog8 小时前
Vue 响应式陷阱与解决方案(第19节)
前端·javascript·vue.js
消失的旧时光-19438 小时前
401 自动刷新 Token 的完整架构设计(Dio 实战版)
开发语言·前端·javascript
console.log('npc')9 小时前
Table,vue3在父组件调用子组件columns列的方法展示弹窗文件预览效果
前端·javascript·vue.js
用户47949283569159 小时前
React Hooks 的“天条”:为啥绝对不能写在 if 语句里?
前端·react.js
我命由我123459 小时前
SVG - SVG 引入(SVG 概述、SVG 基本使用、SVG 使用 CSS、SVG 使用 JavaScript、SVG 实例实操)
开发语言·前端·javascript·css·学习·ecmascript·学习方法
用户479492835691510 小时前
给客户做私有化部署,我是如何优雅搞定 NPM 依赖管理的?
前端·后端·程序员
C_心欲无痕10 小时前
vue3 - markRaw标记为非响应式对象
前端·javascript·vue.js
qingyun98910 小时前
深度优先遍历:JavaScript递归查找树形数据结构中的节点标签
前端·javascript·数据结构
熬夜敲代码的小N10 小时前
Vue (Official)重磅更新!Vue Language Tools 3.2功能一览!
前端·javascript·vue.js