__has_cpp_attribute用于编译期检测编译器是否支持指定标准属性(如likely),返回1或0,必须配合#if使用,不可用于static_assert或函数体内,且不适用于非标准属性(如gnu::hot)或#pragma指令。

用 __has_cpp_attribute 检测编译器是否支持某 attribute(如 [[likely]])
编译期检查“代码是否包含特定编译器指令”,本质是检查该指令是否被当前编译器识别并接受,而非扫描源码字符串。C++20 起,__has_cpp_attribute 是最直接、标准的手段——它在预处理阶段展开为 1 或 0,完全不依赖运行时。
常见误用是拿它去检测用户写的代码里有没有写 [[unlikely]] 这种字面量,这是无效的:__has_cpp_attribute 只判断编译器是否声明支持该 attribute,不是语法解析器。
-
__has_cpp_attribute(likely)判断[[likely]]是否可用(注意括号内无方括号、无双引号) - 必须搭配
#if使用,不能放在函数体内或模板参数中(它是预处理宏) - Clang 和 GCC 12+ 对
likely/unlikely返回 1;MSVC 19.30+ 支持但需 /std:c++20,否则返回 0 - 对非标准 attribute(如
[[gnu::hot]]),要用__has_attribute(hot),不是__has_cpp_attribute
用 static_assert + __has_cpp_attribute 强制要求指令可用
如果某段逻辑**必须**用 [[nodiscard]] 标记返回值,又想确保它真起作用(而非被忽略),就得在编译期堵死不支持的情况。
直接写 static_assert(__has_cpp_attribute(nodiscard), "nodiscard not supported"); 是错的——__has_cpp_attribute 不是常量表达式,不能进 static_assert 参数。正确做法是包裹一层 #if:
立即学习“C++免费学习笔记(深入)”;
#if !__has_cpp_attribute(nodiscard) # error "This library requires [[nodiscard]] support" #endif
这样既避免编译通过后 attribute 被静默忽略,也比运行时报错早得多。注意:GCC 在 -std=c++17 下即使支持 [[nodiscard]],__has_cpp_attribute(nodiscard) 也可能返回 0,必须加 -std=c++17 或更高标准才可靠。
检测非标准指令(如 #pragma GCC optimize)只能靠 __GNUC__ 等宏
像 #pragma GCC optimize("O3") 或 #pragma clang loop vectorize(enable) 这类 pragma 不是 C++ 标准的一部分,没有统一的编译期检测接口。你无法用 __has_cpp_attribute 查它们。
唯一可行方式是组合预定义宏 + 版本号判断:
-
#ifdef __GNUC__且__GNUC__ >= 9→ 大概率支持#pragma GCC optimize -
#ifdef __clang__且__clang_major__ >= 10→#pragma clang loop可用 - MSVC 用
_MSC_VER >= 1920判断是否支持#pragma loop(ivdep) - 所有 pragma 都应配
#pragma GCC push_options/pop_options成对使用,否则可能污染后续代码优化行为
为什么不能用 SFINAE 或 constexpr if 检测 attribute 生效?
有人试图写个模板,把带 [[deprecated]] 的函数塞进去,再用 constexpr if 分支判断——这行不通。attribute 的存在与否不影响类型系统,编译器不会因为加了 [[maybe_unused]] 就让变量类型变成 void 或别的什么。
根本原因是:attribute 是语义修饰符,不是语言结构。它不改变 AST 节点类型,也不参与重载决议或模板推导。所有尝试在模板元编程层面“感知” attribute 是否生效的努力,都会在编译早期失败或得到恒定结果。
真正要验证的是“编译器是否吃这个 directive”,而不是“我的代码里有没有写它”。前者靠预处理器宏,后者只能靠 grep ——但那不是编译期检查,只是文本扫描。


















