随着C++语言的发展,C++11 标准引入了属性规范序列(Attributes),为开发者提供了一种新的编译指示方法。属性规范通过在代码中嵌入特定的指令,帮助编译器优化代码,提升性能,同时提高代码的可读性和安全性。这一特性不仅增强了编程的灵活性,还使得编写高效、易维护的代码变得更加容易。
属性规范序列使用双方括号 [[...]]
包裹,这种语法使得属性指示在代码中显得简洁且不干扰代码的主要逻辑。通过属性规范,开发者可以向编译器传达更多信息,例如建议优化行为、标记废弃的功能或提示特定的代码约束等。
语法
从 C++11 开始,属性可以通过以下语法使用:
c++
[[ attribute-list ]]
从 C++17 开始,还可以指定属性的命名空间:
c++
[[ using attribute-namespace : attribute-list ]]
其中 attribute-list
是由零个或多个属性构成的逗号分隔序列,可能以省略号(...)结束,表示一个包扩展。
属性可以是以下形式:
- 简单属性,例如
[[noreturn]]
。 - 带有命名空间的属性,例如
[[gnu::unused]]
。 - 带有参数的属性,例如
[[deprecated("因某原因")]]
。 - 同时带有命名空间和参数列表的属性。
如果在属性列表的开始使用 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]]
用于提示编译器优化不同的代码路径。
这些示例展示了如何在各种语法环境中使用属性,以便于提高代码的可读性和优化编译过程。
属性规范的扩展使用
属性规范不仅可以应用于函数和变量,还可以用于其他代码元素,如类和模板参数。通过合理使用属性规范,可以大大提高代码的自文档性和维护性。