# How to limit maximum CXX\_STANDARD to prevent accidental C++17 or above code

**URL:** https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862
**Category:** Code
**Created:** [January 16, 2024, 6:57am UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862 "2024-01-16T06:57:27Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![Ryanf55](https://discourse.cmake.org/user_avatar/discourse.cmake.org/ryanf55/32/3516_2.png) [@Ryanf55](https://discourse.cmake.org/u/Ryanf55)
#### Post date: [January 16, 2024, 6:57am UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/1 "2024-01-16T06:57:27Z")

</div>

I have a C++ project built with CMake (an SDK), and a developer team that is used to coding in C++17 on other projects.

Project Requirements:

- Only C++11 or C++14 language features must be used to compile the project. C++17 features that everyone loves like the `<filesystem>` header are prohibited
- The project may require a new version of CMake like 3.28
- The project must be portable to GCC, Clang, Windows, Linux and MacOS (no compile specific or OS specific solutions)

How do I enforce this in the build system, and cause the build to fail if a developer:

1. Tries to modify the SDK and accidentally use C++17 features (happens all the time for the dev team who is used to coding in C++17
2. Links to the SDK and tries to use C++17 in some dependent application code

Both the SDK, and consumers who link to it should be prohibited from using C++17 features.

Here is the minimum example:

**main.cpp**

```auto
#include <filesystem>

int main() {
    return 0;
}

```

**CMakeLists.txt**

```auto
cmake_minimum_required(VERSION 3.24)
project(cpp14_test)

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

add_executable(main main.cpp)

```

With this minimum reproducible example, the build is successful, even though I want it to fail with an include error that `<filesystem>` doesn’t exist. It does in C++17, but our requirements state we can’t use it.

I could come up with a more thorough example with a library and two projects to prove the PUBLIC properties work as expected, however, I think this will suffice for now.

The above method to set the standard is what I read as recommended in Craig Scott’s book, as well as his post on stack overflow, but it only imposes a minimum requirement, not a maximum cap on language features.

Related to:

> [@CMake does not set the compiler option -std to gnu17 or c++17 although I set the target\_compile\_features to cxx\_std\_17](https://discourse.cmake.org/t/cmake-does-not-set-the-compiler-option-std-to-gnu17-or-c-17-although-i-set-the-target-compile-features-to-cxx-std-17/3299):
>
> I updated the versions of GCC and CMake. After the update CMake no longer sets the right command line option for the c++ standard. Here is how I set it: add\_library(project\_options INTERFACE) target\_compile\_features(project\_options INTERFACE cxx\_std\_17) and then I link to the project\_options target\_link\_libraries(app PRIVATE project\_options) With GCC 8.3 (on rhel) this sets the std compile flag to -std=gnu17 Now it is set to -std=c++11 If I set the compiler globally it works as expected: …

> [@Compiler is not using c++17 standard even though I configured it in CMakeLists.txt](https://discourse.cmake.org/t/compiler-is-not-using-c-17-standard-even-though-i-configured-it-in-cmakelists-txt/9570):
>
> Hello, I’m trying to use ROOT’s libraries in Visual Studio Community 2022 v17.8.3. Some of them need the c++17 standard to compile, so I configured it in CMakeLists.txt However, when I try building the project, the compiler gives me a warning because I’m not using the correct standard (obviously followed by a bunch of errors caused by undefined stuff). I tried a lot of different things, like changing th…

[https://cmake.org/cmake/help/v3.23/manual/cmake-compile-features.7.html#requiring-language-standards](https://cmake.org/cmake/help/v3.23/manual/cmake-compile-features.7.html#requiring-language-standards)

---

<div class="post-metadata">

### Author: ![craig.scott](https://discourse.cmake.org/user_avatar/discourse.cmake.org/craig.scott/32/20_2.png) [@craig.scott](https://discourse.cmake.org/u/craig.scott)
#### Post date: [January 16, 2024, 11:58am UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/2 "2024-01-16T11:58:39Z")

</div>

CMake doesn’t provide a way to _limit_ the language standard. It only provides a way to say “this thing needs _at least_ language standard XXX”. A target can have a few different contributors to its required language standard, and CMake chooses the lowest language standard that satisfies all those requirements. It does not give you a way to say “don’t set the language standard to anything higher than XXX”.

> [@Ryanf55](#):
>
> ```auto
> set(CMAKE_CXX_STANDARD 14) 
> set(CMAKE_CXX_STANDARD_REQUIRED ON)
> set(CMAKE_CXX_EXTENSIONS OFF)
> 
> ```
> 
> …The above method to set the standard is what I read as recommended in Craig Scott’s book

That part of the book is going to be updated in the next edition (I’m actually in the middle of updating that specific part right now). With C++20 modules now potentially in the picture, things get a whole lot more complicated. You used to (typically) be able to get away with mixing different language standards in the one build, but you can’t any more once C++20 modules are involved. You need to have the same language standard used throughout or you run into problems with BMIs (built module interfaces). Not directly relevant to your specific query, but you’ll find the recommended pattern of setting the three `CMAKE_...` variables for the language standard and extensions will be more involved going forward.

---

<div class="post-metadata">

### Author: ![scivision](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/a87d85/32.png) [@scivision](https://discourse.cmake.org/u/scivision)
#### Post date: [January 16, 2024, 12:23pm UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/3 "2024-01-16T12:23:33Z")

</div>

Ideas:

- CI job that forces GCC / Clang with c++14 flag
- dirty word source scanner that looks for c++17 includes
- CI jobs that use old enough versions of various target compilers that don’t have most c++17 features while yet having the needed c++11/14 features

The latter approach is the most conventional and would allow you to say the project works all the way to vendor/version sets.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [January 16, 2024, 1:21pm UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/4 "2024-01-16T13:21:04Z")

</div>

> [@Ryanf55](#):
>
> With this minimum reproducible example, the build is successful, even though I want it to fail with an include error that `<filesystem>` doesn’t exist.

Includes are “just” filesystem lookups; I’m not sure how one would enforce this.

The best option is probably to do something like this in a “core” header that “everything” ends up including:

```c++
#if __cplusplus >= 201703L
#error "C++17 is not supported; use C++14 or older"
#endif

```

---

<div class="post-metadata">

### Author: ![Ryanf55](https://discourse.cmake.org/user_avatar/discourse.cmake.org/ryanf55/32/3516_2.png) [@Ryanf55](https://discourse.cmake.org/u/Ryanf55)
#### Post date: [January 16, 2024, 6:38pm UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/5 "2024-01-16T18:38:15Z")

</div>

Thank you all for the suggestions. I’ll try implementing a few of the approaches and share my final approach. Ben’s was the most obvious to add to every file in the public header.

---

<div class="post-metadata">

### Author: ![scivision](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/a87d85/32.png) [@scivision](https://discourse.cmake.org/u/scivision)
#### Post date: [January 16, 2024, 8:09pm UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/6 "2024-01-16T20:09:28Z")

</div>

If you wanted to make it in CMake and not modify the existing source/headers, you could put that little snippet from Ben into a check\_source\_compiles and then fail the CMake configure if check is successful.

---

<div class="post-metadata">

### Author: ![melroy89](https://discourse.cmake.org/user_avatar/discourse.cmake.org/melroy89/32/4091_2.png) [@melroy89](https://discourse.cmake.org/u/melroy89)
#### Post date: [January 21, 2024, 12:43am UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/7 "2024-01-21T00:43:36Z")

</div>

Just curious. What is the rationale to forbid c++ 17?

---

<div class="post-metadata">

### Author: ![Ryanf55](https://discourse.cmake.org/user_avatar/discourse.cmake.org/ryanf55/32/3516_2.png) [@Ryanf55](https://discourse.cmake.org/u/Ryanf55)
#### Post date: [January 21, 2024, 12:46am UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/8 "2024-01-21T00:46:08Z")

</div>

Autosar C++14 and FACE™ 3.0 standards both limit to C++14. If one wants to certify the code, C++17 isn’t allowed, so it would be good to prohibit in CI rather than wait till certification time to realize you accidentally injected a bunch of C++17 code.

> **[Lynx Announces LynxOS-178 Compliance with FACE™ 3.0 Specifications](https://www.lynx.com/press-releases/lynx-announces-lynxos-178-compliance-with-face-3.0-specifications)**
>
> Lynx announces LynxOS-178 compliance with FACE™ 3.0 specifications

> **[C++ in Automotive - AUTOSAR C++14 Coding Guidelines | Parasoft](https://www.parasoft.com/blog/breaking-down-the-autosar-c14-coding-guidelines-for-adaptive-autosar/)**
>
> Researching AUTOSAR C++? ✓ Learn about the AUTOSAR C++ 14 guidelines for Adaptive AUTOSAR & how to leverage automation to expedite software compliance!

---

<div class="post-metadata">

### Author: ![Francesco\_Pretto](https://discourse.cmake.org/user_avatar/discourse.cmake.org/francesco_pretto/32/1902_2.png) [@Francesco\_Pretto](https://discourse.cmake.org/u/Francesco_Pretto)
#### Post date: [May 25, 2025, 3:40pm UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/9 "2025-05-25T15:40:29Z")

</div>

> [@craig.scott](#):
>
> CMake doesn’t provide a way to _limit_ the language standard. […] It does not give you a way to say “don’t set the language standard to anything higher than XXX”

I’ve learnt this the bad way. I think this is problematic: future language updates can (and will) unpredictably break code that is perfectly fine in the current standard today. Some library projects maintainers (including me) would like to not be bothered by library consumers that will attempt to compile the project with a higher standards that is still not supported/tested. While I understand the CMake philosophy of letting the outer list to alter the compile options/flags of the inner list, the language standard/compliance could be seen as a special case and allowed to be enforced in the current scope.

My workaround (tested only in MSVC, so far) is really adding the intended C++ standard to be enforced as a compiler specific compile option:

```auto
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)
if (MSVC)
    add_compile_options(/std:c++17)
else()
    add_compile_options(-std=gnu++17)
endif()

```

I would love CMake to have a feature that does it for me.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [May 26, 2025, 6:16am UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/10 "2025-05-26T06:16:46Z")

</div>

> [@Francesco\_Pretto](#):
>
> I would love CMake to have a feature that does it for me.

If your code should not be “seen” by too-new C++ versions, use the marked-as-solution here. CMake can only enforce for CMake-using projects (neither `pkg-config` nor CPS has any extant concept of generator expressions, so it’s not clear how that gets communicated to non-CMake projects).

---

<div class="post-metadata">

### Author: ![Francesco\_Pretto](https://discourse.cmake.org/user_avatar/discourse.cmake.org/francesco_pretto/32/1902_2.png) [@Francesco\_Pretto](https://discourse.cmake.org/u/Francesco_Pretto)
#### Post date: [May 26, 2025, 7:42am UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/11 "2025-05-26T07:42:49Z")

</div>

> [@ben.boeckel](#):
>
> use the marked-as-solution here.

I’m sorry, I better read the original post, and I discovered my use case is actually different. In my defense the title of the topic is quite misleading. The original poster wanted to prevent **header consumers** of the library/SDK to use C++17 and above. For this use case, the late check in the header as suggested is the certainly the way to go.

In my use case CMake could make a difference:

- Header consumers of the library/SDK can compile with \>= C++17;
- Builders of the library shall compile **exactly** with C++17.

I noticed that when the library is used as a subproject the following is **not** enough to force the library to build in C++17 (and not anything above or below):

```auto
set(CMAKE_CXX_STANDARD 17) 
set(CMAKE_CXX_STANDARD_REQUIRED ON)

```

I also tried `target_compile_features(my_target PUBLIC cxx_std_17)` and also this doesn’t really enforce C++17: a parent list can always inject `add_compile_options(/std:c++20)` or manually edit `CMAKE_CXX_FLAGS`. It’s fine if a library builder want to inject some flags from the parent list, but I believe CMake should have a facility to force the C++ standard in the current list. My workaround above is manually injecting the compilation standard with compiler specific options (I removed the generator expression, which is not so relevant and can make things confusing) and is working fine in MSVC, but please point me out if I missed some other CMake features.

---

<div class="post-metadata">

### Author: ![scivision](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/a87d85/32.png) [@scivision](https://discourse.cmake.org/u/scivision)
#### Post date: [June 25, 2025, 4:02pm UTC](https://discourse.cmake.org/t/how-to-limit-maximum-cxx-standard-to-prevent-accidental-c-17-or-above-code/9862/12 "2025-06-25T16:02:40Z")

</div>

Your workaround is probably the best that CMake could ever reliably do itself, particularly as you note with folks editing CMAKE\_CXX\_FLAGS etc. I would do like you did for the primary targets of your project:

```cmake
if(MSVC)
  set(_std /std:c++17)
else()
  set(_std -std=c++17)
endif()

add_library(main ...)
target_compile_options(main PRIVATE ${_std})

```
