Professional CMake:

A Practical Guide

Go beyond trivial examples and learn how to use CMake effectively with practical advice direct from one of the CMake co-maintainers

Slider

Enabling C++11 And Later In CMake

Updated December 2017

With the recent evolution of C++, build systems have had to deal with the complication of selecting the relevant compiler and linker flags. If your project targets multiple platforms and compilers, this can be a headache to set up. Happily, with features added in CMake 3.1, it is trivial to handle this in a generic way.

Setting the C++ standard directly

The simplest way to use a particular C++ standard in your project is to add the following two variable definitions before you define any targets:

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

Valid values for CMAKE_CXX_STANDARD are 98, 11 and 14, with 17 also being added in CMake 3.8. This variable is used as the default for the CXX_STANDARD target property, so all targets defined after the variable is set will pick up the requirement. The behaviour of the CXX_STANDARD target property mostly behaves as you would expect, adding the relevant compiler and linker flags to the target to make it build with the specified C++ standard. There is a minor wrinkle in that if the compiler doesn’t support the specified standard, rather than causing a warning or error, by default CMake falls back to the latest standard the compiler does support instead. To prevent this fallback behaviour, the CXX_STANDARD_REQUIRED target property should be set to YES. Since you will likely be wanting to disable the fallback behaviour in most situations, you will probably find it easier to just set the CMAKE_CXX_STANDARD_REQUIRED variable to YES instead, since it acts as the default for the CXX_STANDARD_REQUIRED target property. Note that CMake may still end up selecting a more recent language standard than the one specified (see the discussion of compiler features in the next section).

Projects will also probably want to set the following too:

set(CMAKE_CXX_EXTENSIONS OFF)

On some platforms, this results in linking to a different library (e.g. -std=c++11 rather than -std=gnu++11). The default behavior is for C++ extensions to be enabled (credit to ajneu in the comments for pointing out this particular variable and its effects). It is important to be consistent in whether or not extensions are enabled when linking targets, as mixing different settings can result in trying to mix standard library implementations (dangerous at best).

And that’s all it takes to get CMake to set the compiler and linker flags appropriately for a specific C++ standard. In most cases, you probably want to use a consistent C++ standard for all targets defined in a project, so setting the global CMAKE_CXX_STANDARD, CMAKE_CXX_STANDARD_REQUIRED and CMAKE_CXX_EXTENSIONS variables would be the most convenient. Where your CMakeLists.txt might be getting included from some higher level project or if you don’t want to set the global behaviour, then setting the CXX_STANDARD, CXX_STANDARD_REQUIRED and CXX_EXTENSIONS properties on each target will achieve the same thing on a target-by-target basis. For example:

set_target_properties(myTarget PROPERTIES
    CXX_STANDARD 11
    CXX_STANDARD_REQUIRED YES
    CXX_EXTENSIONS NO
)

Setting the C++ standard based on features

For most situations, the above method is the simplest and most convenient way to handle the compiler and linker flags for a particular C++ standard. As an alternative, CMake also offers a finer grained approach, allowing you to specify the compiler features that your code requires and letting CMake work out the C++ standard automatically. Initial support for compiler features was made available in CMake 3.1 for just a small number of compilers, expanded to a broader set of compilers in version 3.2 and from 3.6 all commonly used compilers are supported.

To specify that a particular target requires a certain feature, CMake provides the target_compile_features() command:

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

The PRIVATE, PUBLIC and INTERFACE keywords work just like they do for the various other target_... commands, controlling whether the feature requirement should apply to just this target (PRIVATE), this target and anything that links to it (PUBLIC) or just things that link to it (INTERFACE). The supported feature entries depend on the compiler being used. The full set of features CMake knows about is included in the CMake documentation and can also be obtained from the CMAKE_CXX_KNOWN_FEATURES global property (so you need to use get_property() to access it). To see the subset of features supported by your current compiler, place the following line anywhere after your project() command:

message("Supported features = ${CMAKE_CXX_COMPILE_FEATURES}")

The following example tells CMake that your target requires support for variadic templates and the nullptr keyword in its implementation and its public interface, plus it also uses lambda functions internally:

target_compile_features(myTarget
    PUBLIC
        cxx_variadic_templates
        cxx_nullptr
    PRIVATE
        cxx_lambdas
)

You can also use generator expressions with target_compile_features(). The following example adds a feature requirement for compiler attributes only when using the GNU compiler:

target_compile_features(myTarget
    PUBLIC $<$<CXX_COMPILER_ID:GNU>:cxx_attributes>
)

Using compiler features instead of unilaterally setting the C++ standard for a target has some advantages and disadvantages. On the plus side, you can specify precisely the features you want without having to know which version of the C++ standard supports them. You also get the flexibility of being able to use generator expressions and the language standard requirements are propagated to dependencies through PUBLIC and INTERFACE specifications. One obvious drawback is that the list of features your code uses might be quite long and it would be easy to miss features added to the code later. As a compromise, CMake 3.8 introduced the ability to specify the minimum language standard as a compiler feature. These meta features, as they are called, have names of the form cxx_std_YY, where YY is one of the same values supported by the CXX_STANDARD target property. When building the target, CMake will use the latest language standard required by the CXX_STANDARD target property or by any compiler feature set for that target. In the following example, foo would be built with C++14 and bar with C++17:

set_target_properties(foo PROPERTIES CXX_STANDARD 11)
target_compile_features(foo PUBLIC cxx_std_14)

set_target_properties(bar PROPERTIES CXX_STANDARD 17)
target_compile_features(bar PUBLIC cxx_std_11)

Optional features

Your code base may be flexible enough to support features of later C++ standards if available, but still build successfully without them. For such situations, the compile features approach is convenient because it allows generator expressions to control whether or not a particular feature is used based on whether or not the compiler supports that feature. The following example adapted slightly from the CMake documentation will choose between two different directories to add to the include path for anything linking against myLib:

add_library(myLib ...)
set(with_variadics ${CMAKE_CURRENT_SOURCE_DIR}/with_variadics)
set(no_variadics ${CMAKE_CURRENT_SOURCE_DIR}/no_variadics)
target_include_directories(myLib
    INTERFACE
        "$<$<COMPILE_FEATURES:cxx_variadic_templates>:${with_variadics}>"
        "$<$<NOT:$<COMPILE_FEATURES:cxx_variadic_templates>>:${no_variadics}>"
)

The COMPILE_FEATURES generator expression tests whether the named feature is supported by the compiler. In the above example, if the compiler supports variadic templates, the with_variadics subdirectory will be added to the header search path, whereas if the compiler does not support them, then the no_variadics subdirectory will be added instead. This approach could even be used with the target_sources() command to conditionally include different source files based on compiler features.

Concluding remarks

Where possible, favour simplicity. Compile features are very flexible, but if you know your code requires a specific C++ standard, it is much cleaner to simply tell CMake to use that standard with the CXX_STANDARD target property or the default CMAKE_CXX_STANDARD global variable. This is particularly relevant when linking against third party libraries which may have been built with a specific C++ standard library. If, for example, you link against any Boost modules which have static libraries, you will need to link to the same C++ standard library as was used when Boost was built or else you will get many unresolved symbols at link time.

Also consider using the most recent CMake release your project can require, since later CMake versions have broader support for more compilers (e.g. support for Intel compilers was only added in CMake 3.6). If your project and all the dependencies it links against do not have to support CMake versions earlier than 3.8, the use of compiler features to specify the language standard directly may be considered. Note, however, that unlike target properties, there is no way to set a global default for compiler features, so they would have to be specified on each target that had a requirement for a particular minimum language standard. CMake may then upgrade some targets to later standards if their dependencies demand it through PUBLIC or INTERFACE compiler features.

It is also worth noting that language standard requirements are not restricted to just C++. The various target properties, variables and compiler features have predictably named equivalents for C as well (e.g. C_STANDARD target property, std_c_YY compiler meta feature). CMake 3.8 also introduced language standard specifications for CUDA and the try_compile() command learnt to support language standard requirements.


Get the book for more CMake content

7 thoughts on “Enabling C++11 And Later In CMake

  1. I use

    set(CMAKE_CXX_STANDARD 11) ## or 14 !!
    set(CMAKE_CXX_EXTENSIONS OFF) ## on g++ this ensures: -std=c++11 and not -std=gnu++11

    • Thank you very much for the information. If it weren’t for your comment, I would have never noticed that g++ defaults to GNU standards. 🙂

  2. ” rather than causing a warning or error, by default CMake falls back to the previous standard instead To prevent this fallback behaviour, the CXX_STANDARD_REQUIRED target property should be set to YES.” The default choice is the opposite of what the default choice should be, most people will want CXX_STANDARD_REQUIRED, so why not default that to YES? The few cases where a fallback to the old standard is needed can set CXX_STANDARD_REQUIRED to NO. Seems like a no-brainer? What am I missing?

    • I share your view! I also have a hard time understanding why the default is the way it is, but that’s what the CMake developers chose. You could always ask on the CMake developer’s list, but since it is already released behaviour, I’d be surprised if they were willing to change it.

  3. Hi!

    Great blog post. However there’s one small but very important detail you should mention:

    CXX_STANDARD: For compilers that have no notion of a standard level, such as MSVC, this has no effect.

    It is important to be aware of that, it means you need to take care of MSVC separately – that makes this feature basically useless because it means you have to use the way via target_compile_features anyway to make it work across all compilers.

    • Visual Studio C++ 2015 Update 3 introduced some compiler options to control the standard version (see this issue in Kitware’s issue tracker which mentions this). Prior to this, you were stuck with whatever version was implemented by the MSVC version you were using. In those cases, even target_compile_features() won’t really help you much other than reporting an error if the feature you want isn’t available. I’d expect a future CMake release will add support for those options eventually though.

      In practice, most people understand MSVC is a special case. For a cross-platform project, the understanding is that it wants some minimum C++ standard version. Step 1 is to ensure they are using a compiler that support it on each platform. Step 2 is to use the correct flags to get the desired behaviour and features. In the case of MSVC, step 1 also happens to take care of step 2 because the compiler enables the highest C++ standard it supports by default, which is the opposite to most other common compilers. For all other compilers, the CMake features discussed in this article are necessary to enable C++11 or higher.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: