Apply C++20 to a single CMake target with target_compile_features
Use target_compile_features to require C++20 for one target without forcing the standard on every target in the project.
12 Oct 2026, 19:20 UTC

Problem and takeaway
You have a CMake project where most code compiles with C++17, but one library needs C++20 language features. Setting CMAKE_CXX_STANDARD to 20 upgrades every target in the directory, which can break older code or change behavior for unrelated binaries. The useful takeaway is to use target_compile_features so the C++20 flag applies to only the named target.
Desired outcome
One target, for example parser_lib, compiles with -std=c++20 on GCC/Clang or /std:c++20 on MSVC. Other targets keep their existing standard. The change is limited to the build configuration; no source edits are required beyond CMakeLists.txt.
Prerequisites
- CMake 3.8 or later.
target_compile_featureswas introduced in 3.8. - A
project()call that enables the CXX language. - A compiler that CMake can map to a C++20 flag, such as a recent GCC, Clang, or MSVC.
- Write access to the project directory so CMake can create a build tree.
Procedure
1. Check the CMake version
Run this in a terminal at the project root. No special permissions are required.
cmake --version
Expected output: a version number of 3.8 or higher. If it is lower, upgrade CMake before proceeding.
2. Review the project layout
This example assumes two independent targets in one CMakeLists.txt:
project_root/
CMakeLists.txt
parser.cpp
main.cpp
3. Add per-target standard requirements
Edit CMakeLists.txt. The PRIVATE keyword means the feature applies to parser_lib itself and is not propagated to targets that link against it.
cmake_minimum_required(VERSION 3.8)
project(mixed_standards LANGUAGES CXX)
add_library(parser_lib parser.cpp)
target_compile_features(parser_lib PRIVATE cxx_std_20)
add_executable(cli_tool main.cpp)
target_compile_features(cli_tool PRIVATE cxx_std_17)
cxx_std_20 is a CMake feature name, not a compiler flag. CMake translates it to the correct flag for the detected compiler ID during configuration.
4. Configure and build
Run from the project root. This creates a build/ directory, so you need write access to the current folder.
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build
Expected check: both commands exit with status 0. If the compiler lacks C++20, CMake fails at configure time with a diagnostic instead of silently producing a non-conforming build.
Expected checks
Enable compile command export so you can inspect the exact flags CMake passed to the compiler.
grep -A5 parser_lib build/compile_commands.json | grep -- '--std=c++20'
For MSVC, look for /std:c++20 instead. Then confirm cli_tool does not carry the C++20 flag in its entry.
A negative test is to configure with an older compiler known to lack C++20. CMake should error when it processes target_compile_features(parser_lib PRIVATE cxx_std_20).
Comparison: target scope vs project-wide default
| Approach | Scope | Minimum CMake | Effect |
|---|---|---|---|
target_compile_features(target PRIVATE cxx_std_20) | One named target | 3.8 | Adds the C++20 flag to that target only |
CMAKE_CXX_STANDARD=20 | All targets in the directory | 3.1 | Sets the default standard for every target unless overridden |
Limitations
cxx_std_20requests the flag CMake associates with the feature. It does not by itself prove the compiler or standard library fully implements C++20.- Linking targets compiled with different C++ standards can cause ODR or ABI issues. Keep the standard consistent across linked targets, or verify compatibility carefully.
- Generator expressions are not allowed directly inside
target_compile_features. Usetarget_compile_optionswith conditional expressions for compiler-specific flags. - CMake versions before 3.8 do not recognize
target_compile_features.
Recovery and cleanup
Configuration only writes to the build/ directory; it does not modify system state outside the project. If configuration fails, remove the build directory and revert CMakeLists.txt or lower the requested standard:
rm -rf build
# On Windows, remove the build folder with File Explorer or:
# Remove-Item -Recurse -Force build
After reverting, re-run cmake -S . -B build to confirm the project returns to its previous behavior.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.