属性必须紧挨声明或定义主体,最稳妥位置是函数名后参数列表后、函数体前,或变量声明末尾;标准属性仅4个:[[noreturn]]、[[carries_dependency]]、[[deprecated]]、[[nodiscard]]。

属性说明符写在哪儿才生效
必须紧挨着声明或定义的主体,不能隔开;放在返回类型前、函数名后、参数列表后,都可能失效。最稳妥的位置是紧跟在函数名和参数列表之后、函数体({)之前,或紧跟在变量声明末尾。
常见错误:把 [[nodiscard]] 写在返回类型前面,比如 [[nodiscard]] int foo(); —— 这在 C++17 中合法,但某些旧编译器(如 GCC 7 之前)不识别;更可靠的是写成 int foo() [[nodiscard]];。
-
[[nodiscard]]放在函数声明末尾,适用于函数、枚举、类——编译器才会检查调用处是否忽略返回值 -
[[maybe_unused]]可放在变量、参数、函数前,比如[[maybe_unused]] int x = 42;或void f([[maybe_unused]] int unused_arg) {} - 类模板特化或别名声明上加属性,需放在别名关键字后,如
using T = int [[deprecated]]; // ❌ 错误→ 正确是using T [[deprecated]] = int;
哪些属性是标准且跨编译器可用的
C++11 到 C++20 标准只明确定义了 4 个属性:[[noreturn]]、[[carries_dependency]]、[[deprecated]]、[[nodiscard]]。其中 [[carries_dependency]] 极少用,基本可忽略;其余三个在 Clang、GCC、MSVC 主流版本中行为一致。
像 [[fallthrough]]、[[maybe_unused]]、[[likely]]、[[unlikely]] 是 C++17/C++20 引入的标准属性,但 GCC 8+、Clang 6+、MSVC 2019 16.8+ 才稳定支持。低于这些版本时,它们会被当作未知属性忽略(不报错),但也不会起效。
立即学习“C++免费学习笔记(深入)”;
- 用
[[deprecated]]时建议带消息:[[deprecated("use new_api() instead")]] void old_func(); -
[[fallthrough]]必须单独成行、紧贴case分支末尾,且前面要有break;或return;等终止语句,否则编译器可能不识别 -
[[likely]]和[[unlikely]]只能用于语句(如if、switch分支),不能修饰整个函数
自定义属性或编译器扩展属性怎么安全使用
像 [[gnu::always_inline]]、[[msvc::noinline]] 这类非标准属性,直接写会触发 -Wunknown-attributes 警告,甚至在严格模式下报错。正确做法是用宏封装,按编译器条件启用。
#ifdef __GNUC__
#define ALWAYS_INLINE [[gnu::always_inline]]
#elif defined(_MSC_VER)
#define ALWAYS_INLINE [[msvc::forceinline]]
#else
#define ALWAYS_INLINE
#endif
inline int fast_calc() ALWAYS_INLINE { return 42; }
注意:不要用 #pragma GCC diagnostic ignored "-Wunknown-attributes" 全局压制——它会掩盖真正该修的属性拼写错误(比如把 [[nodiscard]] 手误写成 [[nodisard]])。
- 所有扩展属性必须用完整命名空间(
gnu::、msvc::、clang::),不能省略 - Clang 兼容 GCC 扩展,但 MSVC 不兼容
gnu::,所以跨平台项目务必做宏判断 -
[[no_unique_address]]是标准属性(C++20),但 GCC 10+ 才完全支持空基类优化效果,早期版本仅语法通过
属性和 const/volatile/inline 等说明符的顺序有影响吗
没有语法顺序要求,但可读性和工具链兼容性有实际影响。Clang 和 GCC 接受任意顺序,但 MSVC 对 inline 和属性共存较敏感:把 [[nodiscard]] 放在 inline 后面更稳妥,比如 inline int f() [[nodiscard]];;反过来写([[nodiscard]] inline int f();)在 MSVC 2017 中可能被忽略。
- 函数声明中,
constexpr、static、inline等说明符和属性可以混排,但建议把属性统一放在最后(靠近;或{) - 类成员函数上同时用
const和[[nodiscard]],必须写成int get() const [[nodiscard]];,不能把[[nodiscard]]插在const和;中间以外的位置 - 模板声明中属性要放在模板头之后、函数名之前,如
template<typename T> [[nodiscard]] T make();


















