C++ 中的特性
C++ 标准定义了一组通用属性。 它还允许编译器供应商在其特定的命名空间中定义自己的属性。 但是,编译器只需识别标准中定义的属性。
在某些情况下,标准属性与编译器特定的 __declspec
参数重叠。 在 Microsoft C++ 中,可以使用 [[deprecated]]
属性而不使用 __declspec(deprecated)
。 [[deprecated]]
属性可由任何符合标准的编译器识别。 对于所有其他 __declspec
参数(例如 dllimport
和 dllexport
),到目前为止还没有等效的属性,因此必须继续使用 __declspec
语法。 属性不影响类型系统,也不会改变程序的含义。 编译器会忽略它们无法识别的属性值。
Visual Studio 2017 版本 15.3 和更高版本(适用于 /std:c++17
和更高版本):在属性列表的范围内,可以使用单个 using
引入器为所有名称指定命名空间:
void g() {
[[using rpr: kernel, target(cpu,gpu)]] // equivalent to [[ rpr::kernel, rpr::target(cpu,gpu) ]]
do task();
}
C++ 标准属性
在 C++11 中,属性提供一种使用附加信息来批注 C++ 构造(包括但不限于类、函数、变量和块)的标准化方式。 属性不一定特定于供应商。 编译器可以使用此信息来生成信息性消息,或者在编译属性代码时应用特殊的逻辑。 编译器会忽略它无法识别的任何属性,这意味着你无法使用此语法来定义自己的自定义属性。 属性括在双方括号中:
[[deprecated]]
void Foo(int);
属性代表 #pragma
指令、__declspec()
(Visual C++) 或 __attribute__
(GNU) 等供应商特定扩展的标准化替代项。 但是,在大多数情况下,你仍需使用供应商特定的构造。 标准目前指定了符合标准的编译器应识别的以下属性。
[[carries_dependency]]
[[carries_dependency]]
属性指定函数传播线程同步的数据依赖项顺序。 可将该属性应用于一个或多个参数,以指定传入的参数要将依赖项带入函数主体中。 可将该属性应用于函数本身,以指定返回值要将依赖项带出函数。 编译器可以使用此信息来生成更有效的代码。
[[deprecated]]
Visual Studio 2015 及更高版本:[[deprecated]]
属性指定函数不适合使用。 或者,它可能不存在于库接口的将来版本中。 [[deprecated]]
属性可应用于类、typedef 名称、变量、非静态数据成员、函数、命名空间、枚举、枚举器或模板专用化的声明。 当客户端代码尝试调用该函数时,编译器可以使用此属性来生成信息性消息。 当 Microsoft C++ 编译器检测到使用 [[deprecated]]
项时,会发出编译器警告 C4996。
[[fallthrough]]
Visual Studio 2017 和更高版本:(适用于 /std:c++17
和更高版本。)[[fallthrough]]
属性可在 switch
语句的上下文中用作向编译器(或阅读代码的任何人)显示的提示,指出失败行为是有意的。 Microsoft C++ 编译器当前不会对回退行为发出警告,因此此属性对编译器行为没有影响。
[[likely]]
Visual Studio 2019 版本 16.6 及更高版本:(适用于 /std:c++20
及更高版本。)[[likely]]
属性向编译器指定提示,指出属性化标签或语句的代码路径的执行可能性高于替代项。 在 Microsoft 编译器中,[[likely]]
属性将块标记为“热代码”,这会增加内部优化分数。 针对速度进行优化时,分数增加得更多;针对大小进行优化时,增加的幅度不太大。 净分数会影响内联、循环展开和向量化优化的可能性。 [[likely]]
和 [[unlikely]]
的效果类似于按配置优化,但仅限于当前转换单元的范围。 该属性尚未针对此属性实施块重新排序优化。
[[maybe_unused]]
Visual Studio 2017 版本 15.3 及更高版本:(适用于 /std:c++17
及更高版本。)[[maybe_unused]]
属性指定变量、函数、类、typedef、非静态数据成员、枚举或模板专用化可能是有意不使用的。 当未使用标记为 [[maybe_unused]]
的实体时,编译器不会发出警告。 未使用属性声明的实体以后可以使用属性来重新声明,反之亦然。 分析某个实体的首个标记为 [[maybe_unused]]
的声明后,该实体被视为已标记,适用于当前转换单元的其余部分。
[[nodiscard]]
Visual Studio 2017 版本 15.3 和更高版本:(适用于 /std:c++17
和更高版本。)指定不应该丢弃函数的返回值。 引发警告 C4834,如以下示例中所示:
[[nodiscard]]
int foo(int i) { return i * i; }
int main()
{
foo(42); //warning C4834: discarding return value of function with 'nodiscard' attribute
return 0;
}
[[noreturn]]
[[noreturn]]
属性指定函数永不返回;换言之,它始终引发异常或退出。 编译器可以针对 [[noreturn]]
实体调整其编译规则。
[[unlikely]]
Visual Studio 2019 版本 16.6 及更高版本:(适用于 /std:c++20
及更高版本。)[[unlikely]]
属性向编译器指定提示,指出属性化标签或语句的代码路径的执行可能性低于替代项。 在 Microsoft 编译器中,[[unlikely]]
属性将块标记为“冷代码”,这会减少内部优化分数。 针对大小进行优化时,分数减少得更多;针对速度进行优化时,减少的幅度不太大。 净分数会影响内联、循环展开和向量化优化的可能性。 该属性尚未针对此属性实施块重新排序优化。
Microsoft 特定的属性
[[gsl::suppress(rules)]]
Microsoft 特定的 [[gsl::suppress(rules)]]
属性用于抑制在代码中强制实施准则支持库 (GSL) 规则的检查器发出的警告。 例如,考虑以下代码片段:
int main()
{
int arr[10]; // GSL warning C26494 will be fired
int* p = arr; // GSL warning C26485 will be fired
[[gsl::suppress(bounds.1)]] // This attribute suppresses Bounds rule #1
{
int* q = p + 1; // GSL warning C26481 suppressed
p = q--; // GSL warning C26481 suppressed
}
}
该示例引发了以下警告:
使用安装并激活的 CppCoreCheck 代码分析工具编译此代码时,会触发前两个警告。 但由于使用了该属性,第三个警告不会触发。 可以通过写入 [[gsl::suppress(bounds)]]
而不包括特定规则编号来抑制整个边界配置文件。 C++ Core Guidelines 可帮助你编写更好、更安全的代码。 使用 suppress 属性可以轻松关闭不需要的警告。
[[msvc::flatten]]
Microsoft 特定属性 [[msvc::flatten]]
与 [[msvc::forceinline_calls]]
非常相似,并且可以在相同位置以相同方式使用。 不同之处在于,[[msvc::flatten]]
会递归地 [[msvc::forceinline_calls]]
其应用范围内的所有调用,直到没有调用为止。 这可能会对函数的代码大小增长或编译器的吞吐量产生影响,必须手动管理。
[[msvc::forceinline]]
当放置在函数声明之前时,Microsoft 特定属性 [[msvc::forceinline]]
与 __forceinline
具有相同的含义。
[[msvc::forceinline_calls]]
Microsoft 特定属性 [[msvc::forceinline_calls]]
可以放置在语句或块之上或之前。 它会导致内联启发式尝试 [[msvc::forceinline]]
该语句或块中的所有调用:
void f() {
[[msvc::forceinline_calls]]
{
foo();
bar();
}
...
[[msvc::forceinline_calls]]
bar();
foo();
}
对 foo
的第一次调用以及对 bar
的两次调用都被视为声明为 __forceinline
。 对 foo
的第二次调用不会被视为 __forceinline
。
[[msvc::intrinsic]]
[[msvc::intrinsic]]
属性对其应用到的函数有三个约束:
- 该函数不能具有递归性;它的主体必须只有一个 return 语句,该语句带有从参数类型到返回类型的
static_cast
。 - 该函数只能接受单个参数。
/permissive-
编译器选项是必需的。 (默认情况下,/std:c++20
及更高版本的选项意味着/permissive-
。)
Microsoft 特定的 [[msvc::intrinsic]]
属性告知编译器内联一个元函数,该元函数充当从参数类型到返回类型的命名转换。 当该属性出现在函数定义中时,编译器会用简单的强制转换替换对该函数的所有调用。 [[msvc::intrinsic]]
属性在 Visual Studio 2022 版本 17.5 预览版 2 及更高版本中可用。 此属性仅适用于其后面的特定函数。
示例
在此示例代码中,应用于 my_move
函数的 [[msvc::intrinsic]]
属性使编译器将该函数的调用替换为其主体中的内联静态强制转换:
template <typename T>
[[msvc::intrinsic]] T&& my_move(T&& t) { return static_cast<T&&>(t); }
void f() {
int i = 0;
i = my_move(i);
}
[[msvc::noinline]]
当放置在函数声明之前时,Microsoft 特定属性 [[msvc::noinline]]
与 declspec(noinline)
具有相同的含义。
[[msvc::noinline_calls]]
Microsoft 特定属性 [[msvc::noinline_calls]]
的用法与 [[msvc::forceinline_calls]]
相同。 它可以放置在任何语句或块之前。 它不是强制内联该块中的所有调用,而是对应用到的范围禁用内联。
[[msvc::no_tls_guard]]
Microsoft 特定的 [[msvc::no_tls_guard]]
属性禁止在首次访问 DLL 中的线程局部变量时检查初始化。 默认情况下,在使用 Visual Studio 2019 版本 16.5 及更高版本构建的代码中启用这些检查。 此属性仅适用于其后面的特定变量。 若要全局禁用检查,请使用 /Zc:tlsGuards-
编译器选项。
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈