如何在CMake中启用C++ 11?

416

当我尝试运行CMake生成的makefile以编译我的程序时,我收到以下错误信息:

在C++ 98模式下不支持基于范围的for循环。

我尝试将add_definitions(-std=c++0x)添加到我的CMakeLists.txt文件中,但没有起作用。

我也尝试了这个:

if(CMAKE_COMPILER_IS_GNUCXX)
    add_definitions(-std=gnu++0x)
endif()

当我执行 g++ --version 命令时,会得到以下输出:

g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1

我也尝试过 SET(CMAKE_CXX_FLAGS "-std=c++0x"),但它也不起作用。

我不明白如何在使用 CMake 时激活 C++11 特性。


15
"SET(CMAKE_CXX_FLAGS "-std=c++0x")" 对我来说运行良好,因此 CMakeLists 文件中可能存在其它问题。确保您不会在后续过程中意外覆盖 CMAKE_CXX_FLAGS 的内容。 - ComicSansMS
9
在使用CMake 2.8.8时,add_definitions(-std=c++11)对我有效。 - kyku
@ComicSansMS:你说得对!我覆盖了它,这是我的错误。我已经纠正了它,现在它运行得很好!C++11的东西非常酷!我想循环遍历一个结构体向量,这需要迭代器和不必要的编码噪音,如果我没有范围基于for循环。虽然我猜我可以使用BOOST_FOREACH,但是算了... - Subhamoy S.
44
对于 CMake ≥3.1,set(CMAKE_CXX_STANDARD 11)(在定义目标之前)是最好的方法。 - emlai
2
@tuple_cat 你也可以基于目标进行操作。但是请注意,CXX_STANDARD 在 MSVC 上不起作用,因此如果您想要跨平台的功能,基本上必须退回到 target_compile_features - Ela782
2
关于CMake的问题在SO上很快就会过时。在2020年,您绝对不应该在CMakeLists.txt中纠结编译器标志来做这件事。如果您只想使用C++11、14等构建,请参见MateuszL的答案。如果您还希望具有传播行为(即,您的库的用户必须使用该C++版本进行编译),请参见eyelash的答案 - Alex Reinking
17个回答

481
CMake 3.1发布于2014年,并引入了CMAKE_CXX_STANDARD变量,您可以使用它。如果您知道您将始终可用CMake 3.1或更高版本,您可以直接在顶层CMakeLists.txt文件中编写此内容,或者将其放在任何新目标定义之前。
set (CMAKE_CXX_STANDARD 11)

如果你需要支持古老版本的CMake(现在很少见),这里有一个我想出来的宏,你可以使用:
macro(use_cxx11)
  if (CMAKE_VERSION VERSION_LESS "3.1")
    if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
      set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
    endif ()
  else ()
    set (CMAKE_CXX_STANDARD 11)
  endif ()
endmacro(use_cxx11)

目前,该宏仅支持GCC,但将其扩展到其他编译器应该很简单。
然后,您可以在任何定义使用C++11的目标的CMakeLists.txt文件顶部编写use_cxx11()
CMake问题#15943适用于使用clang目标macOS的用户
如果您正在使用CMake和clang来目标macOS,存在一个bug可能导致CMAKE_CXX_STANDARD功能根本不起作用(不添加任何编译器标志)。请确保您执行以下操作之一:
使用cmake_minimum_required来要求CMake 3.0或更高版本,或者在你的CMakeLists.txt文件的顶部,在project命令之前,使用以下代码将策略CMP0025设置为NEW:
```cmake # 修复在macOS上定位CMAKE_CXX_STANDARD时的行为。 if (POLICY CMP0025) cmake_policy(SET CMP0025 NEW) endif () ```

17
这应该是被接受的答案。它不需要逐个修改每个目标的属性,并且可以跨平台使用。 - DevSolar
5
同意,自CMake 3.1+以来,这应该是被接受的答案。但在Mac上似乎不起作用。你可以通过--stdlib=libc++强制使用--std=c++11,这在Mac上是默认值。但是,CMAKE_CXX_STANDARD总是包括gnu扩展(如果支持),结果似乎无法针对--stdlib=libc++构建。相反,您必须切换到gnu的--stdlib=libstdc++。但是,Mac是特殊情况。对于Linux,选择带有libstdc++的gnu++11是正常的。当然,可以通过一个if(APPLE) add_compile_options()将标志添加到末尾来轻松地进行更正。 - Atif
3
这种方法的一个问题是,即使这些变量被定义为set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang)set(CMAKE_ANDROID_STL_TYPE c++_static),也会强制执行gnu++11。对我来说,唯一可行的方法是经典的set (CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - Antonio
2
在我的macOS上无法工作(CMake 3.9.4,homebrew-clang)。避免其他人感到绝望。不确定为什么@EvanMoran可以使用,但我不能。 - Unapiedra
2
正如@Antonio所提示的,始终附加标志,而不是直接设置,因为这可能会覆盖上游设置(或从命令行传递的设置)。 正确的写法:set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - helmesjo
显示剩余6条评论

194

CMake命令target_compile_features()用于指定所需的C++功能cxx_range_for。然后,CMake将引导使用相应的C++标准。

cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(foobar CXX)
add_executable(foobar main.cc)
target_compile_features(foobar PRIVATE cxx_range_for)

不需要使用add_definitions(-std=c++11)或修改CMake变量CMAKE_CXX_FLAGS,因为CMake会确保使用适当的命令行标志调用C++编译器。

也许你的C++程序使用了比cxx_range_for更多的C++特性。CMake全局属性CMAKE_CXX_KNOWN_FEATURES列出了你可以选择的C++特性。

你可以通过设置CMake属性CXX_STANDARDCXX_STANDARD_REQUIRED来明确指定C++标准,而不是使用target_compile_features()

详见我的更详细的回答


4
今天的编辑似乎有误导性。CMake 3.0.0没有包含target_compile_features命令。如果我错了,请纠正我。我认为这个命令只存在于CMake的夜间版本构建中。 - Erik Sjölund
4
我会说这是最准确的答案。 - Michał Walenciak
11
我认为这就是应该的做法。其他答案只是手动添加标志,从而引入了不兼容性。然而,这似乎只在CMake 3.1及以上版本中可用。 - Uli Köhler
2
@UliKöhler,这个方法目前实际上仍然不可用,并且可能在一些编译器中出现在3.2版本中。短期内不要使用此方法;它完全不可移植。 - Doug
2
有没有想法如何在CMake 2.6中实现这个? - marknuzz
显示剩余4条评论

92

我正在使用

include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
        message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()

但如果你想使用 C++11g++ 4.6.1版本相当老旧。建议升级到新一点的 g++ 版本。


4
对于我来说,这是唯一正确和好的回答,使用最新的Linux系统上已实施的cmake,并使用g++编译器。 - Patrick B.
1
复制并粘贴这个代码,它完美地运行了。我正在使用CMAKE 2.8.9的Cygwin。我知道这里大部分方法是因为我关注CMAKE邮件列表,并且我已经将WebKit移植到各种编译器上。我们为WebKit端口所做的事情是安装CMake 2.8.12。然而,由于我知道Cygwin的CMAKE版本较旧,我想要适用于该版本的东西。(抱歉,不是将WebKit移植到Cygwin) - cardiff space man
很好,这是一个适用于旧版CMake和g++ 4.6(并且具有未来兼容性)的插件。我也点赞了基于CXX_STANDARD的答案,但这是我情况下唯一有用的答案。 - Tomasz Gandor
这正是我在寻找的,但不清楚使用该模块所需的最低cmake版本是多少。 cmake --help-module 对此并没有太大帮助。 - Jan Segre
1
@JanSegre 这个模块最早出现在2.4版本中,http://www.cmake.org/gitweb?p=cmake.git;a=commitdiff;h=72b38e3aa7ba5fa4a70144e05de36bc99fda173a - KoKuToru
谢谢,它运行得非常好。对于使用GCC 4.6的任何人来说,需要注意的是,大多数我尝试过并依赖于C++11的代码都会出现错误。 - Elias Kouskoumvekakis

66

设置 Cxx 标准的最简单方法是:

 set_property(TARGET tgt PROPERTY CXX_STANDARD 11)

更多详情请参考CMake文档


3
是的,这绝对看起来是现代 CMake(3.1+)中实现它的最佳方式之一。 - Erbureth
20
你可以直接使用set(CMAKE_CXX_STANDARD 11)来定义默认属性,这会影响所有在该语句之后创建的目标。 - emlai
4
如果您想在不同的目标上设置不同的C++标准,那么这也是您需要的解决方案,因为@emlai建议的set命令是全局的,并会影响所有后续的目标。 - Elliott Slaughter

46

在现代 CMake(>= 3.1)中设置全局要求的最佳方式是:

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

它翻译成“我希望所有目标都能使用C++11,这不是可选的,我不想使用GNU或Microsoft扩展。” 我认为在C++17中,这仍然是最好的方法。

来源:在CMake中启用C++11及更高版本


1
如果您没有最新的CMake,那么这种方法对于最新版本的C++来说并不理想。例如,在CMake 3.12之前,您无法通过这种方式启用C++2a。 - Ruslan
2
没有理由不使用最新的CMake。Chocolatey、Homebrew和Snap都有最新的软件包。 - Alex Reinking
请注意,在定义任何目标之前必须设置这些内容;如果未能这样做,将导致仅受到在设置它们之后定义的目标受影响。这是可以理解的,因为它们是默认值。 - legends2k
不再使用“更现代的cmake = 3.12”... 更喜欢使用 target_compile_options(project_name PRIVATE option1 ...)target_compile_definitions 或仍然使用 target_compile_features(ao-project_name PRIVATE some_feature),如果您的目标在C++11和C++17之间,但不超过这个范围。 - Sandburg
1
@Sandburg你认为我为什么更喜欢分别为多个目标指定多个功能呢?你认为在单个项目中为不同的目标设置不同的期望是常见的吗?我知道这个特性,但说实话,我无法想到单个用例,这是期望的主要方法时会非常理想的。 无论如何:根据链接的文章,在3.1中可以使用此功能。 - MateuszL

43

如果您使用的是 CMake 3.8 或更高版本,您可以使用以下命令:

target_compile_features(target PUBLIC cxx_std_11)

如果你希望当工具链无法遵守此标准时,使生成步骤失败,你可以将其设为必需。

set_target_properties(target PROPERTIES CXX_STANDARD_REQUIRED ON)

如果您希望严格符合标准C++,即避免使用编译器提供的C++扩展(例如GCC的-std=gnu++17),请另外设置。

set_target_properties(target PROPERTIES CXX_EXTENSIONS OFF)

这个详细记录在现代CMake介绍中——>添加特性——>C++11及更高版本。如果您受到旧版CMake的限制,它还提供了如何在旧版CMake上实现此目标的建议。


3
这是CMake最新版本推荐的方式。 - camino
1
这里的 PUBLIC 函数是什么作用? - Rotsiser Mho
3
@RotsiserMho:PUBLIC 表示依赖于你目标的其他目标也将使用 C++11。例如,如果你的目标是一个库,那么所有使用 target_link_libraries 链接到你的库的目标都将被编译为支持 C++11。 - eyelash
3
目前来看,这是技术上最好的定义标准版本的方式,应该被接受为答案(尽管埃里克的方法仍然可行,如果需要更精细的设置)。 - Razakhel
有没有一种方法可以设置全局/CMAKE_默认目标特性?还是说我们必须为每个目标重复设置语言标准? - Masquue

40

事实证明,SET(CMAKE_CXX_FLAGS "-std=c++0x")确实激活了许多C++11特性。它没有起作用的原因是语句看起来像这样:

set(CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS} -g -ftest-coverage -fprofile-arcs")

采用这种方法后,-std=c++0x 标志被覆盖了,因此它不起作用。逐个设置标志或使用列表方法是有效的。

list( APPEND CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS} -g -ftest-coverage -fprofile-arcs")

38
我通常只使用以下代码:SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # 适用于gcc版本 >= 4.7,或者c++0x适用于4.6 - David Doria
我曾为此编写了一个脚本(尽管不完整):https://github.com/Morwenn/POLDER/blob/master/cmake/set_cxx_norm.cmake - Morwenn
11
如果你在命令行上指定了任何CMAKE_CXX_FLAGS,第二种方法将在构建命令中产生一个分号,并重复原始的CMAKE_CXX_FLAGS两次。 - Nikolai
不要手动添加-g标志,而是应将CMAKE_BUILD_TYPE变量设置为debug:http://voices.canonical.com/jussi.pakkanen/2013/03/26/a-list-of-common-cmake-antipatterns/ - bames53
4
“CXX_STANDARD”属性更好,因为它不依赖于具体的编译器。 - Mariusz Jaskółka

18
最简单的方法是:

add_compile_options(-std=c ++ 11)

添加编译选项。

5
仅限于CMake 3.0及以上版本可用。 - Raúl Salinas-Monteagudo
5
在同一项目中编译C文件时,这会导致警告和错误。 - rosewater
绝对不要这样做。这会破坏编译纯C程序。它将你的CMakeLists.txt与特定的编译器粘合在一起,而且内置支持也可以实现这一点。 - Alex Reinking
1
这在Visual Studio编译器上失败了。查看我的答案,以获取适用于所有编译器的更好的解决方案:https://dev59.com/u2gv5IYBdhLWcg3wF89P#69804542 - Amin Ya

14

这是启用C++11支持的另一种方式,

ADD_DEFINITIONS(
    -std=c++11 # Or -std=c++0x
    # Other flags
)

我遇到过只有这种方法有效,而其他方法失败的情况。也许与CMake的最新版本有关。


11
只有在你仅使用C++编译器时才能有效。如果你还在使用CC编译器,它就会失败。 - Emmanuel
7
"add_definitions" 只应该用于添加宏定义,即 -D SOMETHING。正如 @Emmanuel所说,它在许多情况下并不起作用。 - xuhdev
我以前用过这个,但是当我包含一个C文件时遇到了问题,因为add_definitions不适用于设置标志。 - Jan Segre

6

现代的cmake提供了更简单的方法来配置编译器使用特定版本的C ++。任何人需要做的唯一的事情就是设置相关的目标属性。在cmake支持的属性中,用于确定如何配置编译器以支持特定版本的C ++ 的属性如下:

  • CXX_STANDARD 设置请求构建目标的C++标准版本的功能。将其设置为11以对C++11进行目标设置。

  • CXX_EXTENSIONS,一个布尔值,指定是否请求编译器特定的扩展。 将其设置为Off将禁用对任何编译器特定扩展的支持。

为了演示,这里有一个CMakeLists.txt的最小工作示例。

cmake_minimum_required(VERSION 3.1)

project(testproject LANGUAGES CXX )

set(testproject_SOURCES
    main.c++
    )

add_executable(testproject ${testproject_SOURCES})

set_target_properties(testproject
    PROPERTIES
    CXX_STANDARD 11
    CXX_EXTENSIONS off
    )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接