CMAKE_CXX_STANDARD必须放在project()之后,因为project()才初始化编译器检测和语言支持逻辑;此前设置无效,后续target将回退到C++98标准。

直接在 CMakeLists.txt 里写 set(CMAKE_CXX_STANDARD 17) 就行,但必须放在 project() 之后、所有 add_executable() 或 add_library() 之前,否则部分 target 会漏掉。
为什么 CMAKE_CXX_STANDARD 必须放在 project() 之后?
CMake 在 project() 调用时才初始化编译器检测和语言支持逻辑。如果把 set(CMAKE_CXX_STANDARD 17) 放在它前面,CMake 还没识别出你用的是 C++ 编译器,这个设置就相当于白写——后续 add_executable() 创建的 target 会回退到默认的 98 标准(不是 11!老项目升级时特别容易栽在这儿)。
常见错误写法:
set(CMAKE_CXX_STANDARD 17) project(MyApp LANGUAGES CXX) add_executable(app main.cpp)
正确顺序:
立即学习“C++免费学习笔记(深入)”;
cmake_minimum_required(VERSION 3.10) project(MyApp LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_executable(app main.cpp)
-
CMAKE_CXX_STANDARD_REQUIRED ON:让 CMake 在 configure 阶段就报错,而不是悄悄降级到c++14或c++11 -
CMAKE_CXX_EXTENSIONS OFF:禁用-std=gnu++17,强制用-std=c++17,避免 GNU 特有语法污染可移植性
不同 C++ 标准值的实际效果差异
CMAKE_CXX_STANDARD 可设为 11、14、17、20、23,但生效前提是 CMake 版本和编译器都支持。例如:
- CMake 3.20+ 才完整支持
23;低于此版本设了也无效,configure 阶段可能静默忽略 - GCC 7 支持
c++17,但不支持c++20的部分特性;设set(CMAKE_CXX_STANDARD 20)+REQUIRED ON会让 configure 直接失败 - MSVC 对
CMAKE_CXX_EXTENSIONS OFF响应较弱,但它会影响某些跨平台宏(比如__GNUC__是否定义),关掉更稳妥
注意:CMAKE_CXX_STANDARD 是全局开关,影响所有后续定义的 target。如果你有一个模块必须用 c++11,而主程序要用 c++20,不能靠它分治。
单个 target 覆盖全局标准怎么写?
用 set_property() 给特定 target 单独设标准,优先级高于全局 CMAKE_CXX_STANDARD:
add_library(old_module src/old.cpp) set_property(TARGET old_module PROPERTY CXX_STANDARD 11) set_property(TARGET old_module PROPERTY CXX_STANDARD_REQUIRED ON) add_executable(main main.cpp) set_property(TARGET main PROPERTY CXX_STANDARD 20)
这种写法比 target_compile_features() 更底层、更确定——后者只声明“我需要哪些特性”,CMake 会按需提升标准;而 set_property(... PROPERTY CXX_STANDARD ...) 是硬性指定编译器标志。
容易被忽略的一点:这些 set_property() 必须写在对应 add_* 之后,否则 target 还不存在,CMake 会报 Cannot set property on non-existent target 错误。
旧项目迁移时最常踩的坑
很多老 CMakeLists.txt 用 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 手动拼 flag,这会绕过 CMake 的标准感知机制,导致 target_compile_features() 失效、IDE(如 CLion)无法正确解析语法、生成的 Visual Studio 工程里 /std:c++17 可能没生效。
现代 CMake(3.1+)应该完全避免直接操作 CMAKE_CXX_FLAGS。真正需要微调编译选项时,用 target_compile_options() 针对具体 target 加 flag,而不是全局污染。
总结一句:标准版本不是“加个 flag”那么简单,它是 CMake 构建图里一个带依赖关系的元属性,位置、顺序、配套开关缺一不可。


















