CMake笔记-01-设置C++标准

本文最后更新于 2023年3月25日 下午

引言

C++ 语言有不同版本的标准,如 C++ 98、C++ 11、C++ 14 等。不同标准提供的特性有所区别,故经常有必要在 CMakeLists.txt 中为项目指定使用何种标准,进而让 CMake 使用正确的编译器标志(比如 -std=c++11)。对于 CMake v3.1 以上版本,可以通过 CMAKE_CXX_STANDARD 变量指定标准,或通过 target_compile_features 函数根据使用的特性自动推断适用于目标的编译器标志。

在早期版本的 CMake 中指定标准,方法参见 C++ Standard Common Method

CMAKE_CXX_STANDARD 变量

CMAKE_CXX_STANDARD 变量用于设置创建目标时使用的 CXX_STANDARD 目标属性默认值。

基本使用

下面是一个简单的例子,演示如何指定使用 C++ 11 标准:

1
2
3
4
# 项目目录结构
[A-hello-cmake]$ tree .
├── CMakeLists.txt
└── main.cpp

项目文件只有两个:包含 CMake 命令的 CMakeLists.txt、一个使用 C++ 11 标准的简单 “Hello World” 源文件 main.cpp

其中 CMakeLists.txt 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
# 设置允许使用的 CMake 最小版本(不能低于 3.1)
cmake_minimum_required(VERSION 3.1)

# 设置项目名称
project (hello_cpp11)

# 将 C++ 标准设置为 C++ 11
set(CMAKE_CXX_STANDARD 11)

# 添加 executable
add_executable(hello_cpp11 main.cpp)

在第 8 行中通过设置变量指定了该项目使用的 C++ 标准,当前所有可用的标准可以在 CXX_STANDARD 中查询。

强制指定标准

出于对编译器的兼容性之考虑,上面这种设置标准的方法并不是强制执行。假设某个项目设置使用 C++ 11 标准,但用户使用的编译器并不支持 -std=gnu++11 (或等价的)标志,将不会导致错误或警告,而是在允许的情况下添加 -std=gnu++98 标志。换言之,CMake 将自动“衰减”至最接近的标准。如果确实需要强制指定标准,禁用这种自动衰减调整,那么可以通过设置 CXX_STANDARD_REQUIRED 实现。

CXX_STANDARD_REQUIRED 是一个布尔类型的变量,用于描述是否需要(强制)指定 CXX_STANDARD。当打开此选项,且当前使用的编译器不支持指定的标准时,会在 configuring 阶段报错失败,不会进行编译。

对上一节中的小例子稍加修改,使其强制使用 C++ 11 标准:

1
2
3
4
5
6
7
8
cmake_minimum_required(VERSION 3.1)

project (hello_cpp11)

set(CMAKE_CXX_STANDARD 11) # 将 C++ 标准设置为 C++ 11
set(CMAKE_CXX_STANDARD_REQUIRED ON) # C++ 11 是强制要求,不会衰退至低版本

add_executable(hello_cpp11 main.cpp)

禁用编译器特有扩展

另一个经常与 CMAKE_CXX_STANDARD 一同设置的布尔变量是 CMAKE_CXX_EXTENSIONS。该属性用于指定是否使用编译器特有的扩展(即,不同的编译器在 C++ 标准之外自行实现的、非通用的特性)。对于某些编译器来说,启用此选项后会在编译行中添加特殊的标志,比如用 -std=gnu++11 替换 -std=c++11。该属性默认为 ON。如果某个项目对可移植性有较高的要求,可能为不同的平台使用不同的编译器,那么建议将其设置为 OFF

继续更新完善上面的示例:

1
2
3
4
5
6
7
8
9
cmake_minimum_required(VERSION 3.1)

project (hello_cpp11)

set(CMAKE_CXX_STANDARD 11) # 将 C++ 标准设置为 C++ 11
set(CMAKE_CXX_STANDARD_REQUIRED ON) # C++ 11 是强制要求,不会衰退至低版本
set(CMAKE_CXX_EXTENSIONS OFF) # 禁止使用编译器特有扩展

add_executable(hello_cpp11 main.cpp)

目标级设置

在上面几节的例子中,均是通过 set(CMAKE_CXX_* value) 的方式设置 CXX_* 全局级别的变量来实现,这也是最为推荐的做法。在极少数情况下,可能需要为项目中的不同目标设置不同的 C++ 标准(比如某项目其实更像是由几个较独立的子部分构成、且这些不同的部分不能使用相同的标准),可以通过设置目标属性的方法来为每个目标细致调整:

1
2
3
4
5
set_target_properties(myTarget PROPERTIES
CXX_STANDARD 11
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)

再次强调,除非有非常充分的理由,否则不要这样设置。

target_compile_features 函数

target_compile_features 函数用于将期望的编译器特性添加到目标中。其完整语法为:

1
target_compile_features(<target> <PRIVATE|PUBLIC|INTERFACE> <feature> [...])

指定编译一个给定目标时需要的编译器特性。如果该特性没有在 CMAKE_C_COMPILE_FEATURESCMAKE_CUDA_COMPILE_FEATURESCMAKE_CXX_COMPILE_FEATURES 变量中列出,那么 CMake 将报告一个错误。如果使用该特性需要一个额外的编译器标志,例如 -std=gnu++11,该标志将被自动添加。

INTERFACEPUBLICPRIVATE 关键字是用来指定特性的范围的。PRIVATEPUBLIC 项将填充 <target>COMPILE_FEATURES 属性。PUBLICINTERFACE 项目将填充 <target>INTERFACE_COMPILE_FEATURES 属性。对同一个 <target> 的重复调用会追加项目。

下面仍然以一个简单的小例子来演示如何通过该函数为目标设置合适的 C++ 标准。

项目文件结构与上一节中的完全相同:

1
2
3
4
# 项目目录结构
[A-hello-cmake]$ tree .
├── CMakeLists.txt
└── main.cpp

CMakeLists.txt 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 设置允许使用的 CMake 最小版本(不能低于 3.1)
cmake_minimum_required(VERSION 3.1)

# 设置项目名称
project (hello_cpp11)

# 添加 executable
add_executable(hello_cpp11 main.cpp)

# 将 C++ 标准设置为使用自动的合适标准
target_compile_features(hello_cpp11 PUBLIC cxx_auto_type)

# 打印此版本的 CMake 的已知编译特性列表
message("List of compile features: ${CMAKE_CXX_COMPILE_FEATURES}")

参考资料


CMake笔记-01-设置C++标准
https://muzing.top/posts/4afe95dc/
作者
Muzing
发布于
2023年3月24日
更新于
2023年3月25日
许可协议