第7章:设置C++标准——用CMAKE_CXX_STANDARD控制编译标准
说实话,C++标准这个话题,我每次讲课都要强调一遍。为什么呢?因为我在实际项目中见过太多因为标准不一致导致的“灵异事件”——明明本地编译好好的,到了同事机器上就报错;或者代码里用了C++17的特性,结果编译环境只支持C++11,那场面,真是尴尬到脚趾抠地。
所以这一章,咱们就聊聊怎么用CMake来精准控制C++标准。说白了,就是告诉编译器:“嘿,哥们儿,按这个标准来编译!”
7.1 为什么需要显式设置C++标准?
你想想看,C++从98到11、14、17、20,再到23,每个版本都加了新特性。比如C++11的auto、nullptr,C++14的泛型Lambda,C++17的if constexpr,C++20的协程和概念。
如果不指定标准,编译器会按默认标准来。不同编译器、不同版本的默认标准可能不一样。这就埋下了隐患。
核心原则:项目必须明确声明它需要哪个C++标准。这是可移植性的基础。
7.2 核心变量:CMAKE_CXX_STANDARD
CMake提供了三个变量来控制C++标准:
| 变量名 | 作用 | 取值 |
|---|---|---|
CMAKE_CXX_STANDARD |
指定C++标准版本 | 11, 14, 17, 20, 23 |
CMAKE_CXX_STANDARD_REQUIRED |
是否强制要求该标准 | ON / OFF |
CMAKE_CXX_EXTENSIONS |
是否允许编译器扩展 | ON / OFF |
用法很简单,在CMakeLists.txt顶部加上:
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
这三行一写,CMake就会强制编译器使用C++17标准,并且不允许GCC的-std=gnu++17这种扩展模式。
我的习惯:我个人习惯把这三行放在project()命令之后,add_executable之前。这样整个项目都统一标准,不会出现某个子目录偷偷用不同标准的情况。
7.3 全局设置 vs 目标设置
上面那种是全局设置,影响所有目标。但有时候,你只想给某个特定库或可执行文件设置标准。比如项目主体用C++17,但某个遗留模块只能用C++11。
这时候可以用目标属性:
add_library(my_legacy_lib legacy.cpp)
set_target_properties(my_legacy_lib PROPERTIES
CXX_STANDARD 11
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)
add_executable(my_app main.cpp)
set_target_properties(my_app PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)
这样两个目标各用各的标准,互不干扰。嗯,这里要注意:如果目标之间有依赖关系,比如my_app链接了my_legacy_lib,那my_app的头文件如果用了C++17特性,而my_legacy_lib只支持C++11,编译时就会报错。所以混用标准时要格外小心。
7.4 避坑指南:我曾经踩过的坑
我曾经在一个跨平台项目里,只设置了CMAKE_CXX_STANDARD 17,但没设CMAKE_CXX_STANDARD_REQUIRED。结果在macOS上,Clang默认用了C++14,而代码里用了C++17的std::filesystem。编译通过了,但链接时报了一堆莫名其妙的错误。我查了整整一天,最后发现是标准不一致导致的。
从那以后,我养成了一个习惯:只要设了CMAKE_CXX_STANDARD,就一定把CMAKE_CXX_STANDARD_REQUIRED设为ON。这样如果编译器不支持指定标准,CMake会直接报错,而不是偷偷降级。
重要提醒:如果不设CMAKE_CXX_STANDARD_REQUIRED,CMake会尝试使用指定标准,但如果编译器不支持,它会静默降级到下一个可用的标准。这非常危险!
7.5 编译器扩展:要不要关掉?
CMAKE_CXX_EXTENSIONS这个变量,默认是ON。打开后,GCC会用-std=gnu++17而不是-std=c++17。GNU扩展里有一些非标准的东西,比如typeof、变长数组等。
我个人建议:除非你有特殊理由,否则把它设为OFF。原因很简单——可移植性。用了GNU扩展,代码就绑死在GCC上了。万一哪天要切换到Clang或MSVC,你就得改代码。
当然,如果你在写Linux内核模块或者某些底层库,确实需要GNU扩展,那就另当别论了。
7.6 知识体系一览
下面这张图,帮你理清本章的核心逻辑:
7.7 实战示例:一个完整的CMakeLists.txt
最后,给你一个可以直接用的模板:
cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 添加可执行文件
add_executable(my_app main.cpp)
# 添加库
add_library(my_lib lib.cpp)
set_target_properties(my_lib PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)
这个模板里,主程序用C++17,库用C++14。实际项目中,我建议尽量统一标准,除非有不得已的理由。毕竟,维护两套标准的心智负担,你懂的。
好了,关于C++标准的设置,就聊到这儿。记住三个变量、两种方式、一个原则:明确声明,强制要求,关闭扩展。这样你的项目就能在各种编译器、各种平台上保持一致的行为了。
公众号:蓝海资料掘金营,微信deep3321