最可靠方式是按优先级顺序检测预定义宏:先判 _MSC_VER(MSVC 或 clang-cl),再判 __clang__(原生 Clang),最后判 __GNUC__(GCC/MinGW);版本判断须用对应宏区间比较,避免硬匹配或依赖非稳定宏。

直接用预定义宏判断编译器版本最可靠,不依赖外部工具或运行时检测;但必须注意宏的定义范围、组合逻辑和比较方式,否则在跨平台或混合编译环境(如 Clang on Windows)下极易误判。
如何区分 GCC / Clang / MSVC 编译器类型
不能只靠 __GNUC__ 或 __clang__ 单独存在来断定编译器——Clang 在 Windows 上常同时定义 __clang__ 和 _MSC_VER,而 MinGW 下的 GCC 也可能定义 _WIN32。正确做法是按优先级顺序检测:
- 先查
_MSC_VER:只要它被定义,就认为是 MSVC 或兼容模式(如 clang-cl),无需再看其他宏 - 再查
__clang__:若未定义_MSC_VER但定义了__clang__,才是原生 Clang - 最后查
__GNUC__:仅当以上两个都未定义时,才当作 GCC(含 MinGW)
示例片段:
#if defined(_MSC_VER)
// MSVC or clang-cl
#elif defined(__clang__)
// Native Clang
#elif defined(__GNUC__)
// GCC or MinGW-GCC
#else
// unknown compiler
#endif
GCC 版本判断:用 __GNUC__ 系列宏做区间比较
__GNUC__、__GNUC_MINOR__、__GNUC_PATCHLEVEL 是 GCC 工具链定义的标准宏,值为整数,可安全用于数值比较。不要写成 #if __GNUC__ == 12 这种硬匹配——补丁版本差异可能导致行为不一致,应使用 >= 区间判断:
立即学习“C++免费学习笔记(深入)”;
-
#if __GNUC__ >= 12表示 GCC 12.x 及以上(含 12.1、12.4) -
#if __GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ >= 3)表示 ≥ 11.3 - 避免单独依赖
__GNUC__:GCC 10 和 11 对 C++20concept支持程度不同,需结合语言标准宏或特性宏(如__cpp_concepts)验证
MSVC 版本判断:只认 _MSC_VER,别碰 _MSC_FULL_VER
_MSC_VER 是 MSVC 唯一稳定、文档化且跨 VS 版本可比的宏,其值对应主版本号(如 VS2019 是 1920,VS2022 是 1930)。它在 clang-cl 模式下也会被定义,这反而是优势——表示你处在 MSVC 兼容 ABI 和头文件环境中。
- 用
#if _MSC_VER >= 1930判断是否支持 VS2022+ 新增的 STL 特性(如std::span的 constexpr 构造) - 不要用
_MSC_FULL_VER:它包含构建号,不同 Update 版本值跳跃无规律,不适合作为条件编译依据 - 别混淆
WINVER或_WIN32_WINNT:它们控制 Windows SDK 行为,和编译器版本无关
Clang 版本判断:优先用 __clang_major__ 系列,慎用 __clang__
__clang__ 是个整型宏(值为 1),只表示“是 Clang”,不能反映版本。真正可用的是 __clang_major__、__clang_minor__、__clang_patchlevel__。它们在 Clang 3.5+ 全面可用,且语义清晰:
-
#if __clang_major__ >= 15表示 Clang 15.0 及以上 - Clang on Windows 下若同时定义
_MSC_VER,应以_MSC_VER为准——此时你实际在用 MSVC 头文件和链接器,Clang 只是前端 - Clang 的
__cplusplus值可能滞后于实际支持能力(如 Clang 14 报告 C++17,但已部分支持 C++20),建议搭配语言特性宏(如__cpp_if_constexpr)双重验证
跨编译器版本判断最易出错的地方,不是宏写错,而是忽略了编译器共存场景(如 Clang + MSVC headers)和语言标准与实现进度的脱节。宁可多一层 defined() 检查,也不要靠排除法倒推编译器身份。


















