Feature Testing Macros

The feature testing macros is a relatively unknown feature in C + + 20. They give you the definitive answer to which C++ feature your compiler supports.

When you try out the latest C++ features, you often get errors. Now the question is: who is to blame?

  1. Did you make a mistake?
  2. Does your compiler support this feature?
  3. Did you find a compiler bug?

Option 3 occurs rarely, so you have only one question to answer: Does your compiler support this feature?

This question is easy to answer thanks to the cppreference and the feature testing macros.

C++ Compiler Support

As so often, cppreference.com/compiler_support answers questions about the core language and library, including the C++11 standards up to C++ 26.

The following tables give you an overview of the core language and library support for the C++26 standard.

C++26 Core Language

C++26 Library

Are the answers not specific enough? Let me jump to the feature testing macros in C++20.

Feature Testing macros

The header <version> allows you to ask your compiler for its C++11 or later support. You can ask for attributes, features of the core language, or the library. <version> has more than 300 macros defined, which expand to a number when the feature is implemented. The number represents the year and month the feature was added to the C++ draft standard. These are the numbers for static_assert, lambdas, and concepts.

__cpp_static_assert 200410L __cpp_lambdas 200907L __cpp_concepts 201907L

The page feature testing macros shows all macros.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Embedded Programming with Modern C++": January 2025
  • "Generic Programming (Templates) with C++": February 2025
  • "Clean Code: Best Practices for Modern C++": May 2025
  • Do you want to stay informed: Subscribe.

     

    The following program, adopted from the cppreference, displays all C++20 core language macros. The flag /Zc:__cplusplus enables the feature testing macro on Windows.

    // featureTesting20.cpp
    // from cppreference.com
    
    #if __cplusplus < 201100
    #  error "C++11 or better is required"
    #endif
     
    #include <algorithm>
    #include <cstring>
    #include <iomanip>
    #include <iostream>
    #include <string>
     
    #ifdef __has_include
    # if __has_include(<version>)
    #   include <version>
    # endif
    #endif
     
    #define COMPILER_FEATURE_VALUE(value) #value
    #define COMPILER_FEATURE_ENTRY(name) { #name, COMPILER_FEATURE_VALUE(name) },
     
    #ifdef __has_cpp_attribute
    # define COMPILER_ATTRIBUTE_VALUE_AS_STRING(s) #s
    # define COMPILER_ATTRIBUTE_AS_NUMBER(x) COMPILER_ATTRIBUTE_VALUE_AS_STRING(x)
    # define COMPILER_ATTRIBUTE_ENTRY(attr) \
      { #attr, COMPILER_ATTRIBUTE_AS_NUMBER(__has_cpp_attribute(attr)) },
    #else
    # define COMPILER_ATTRIBUTE_ENTRY(attr) { #attr, "_" },
    #endif
     
    // Change these options to print out only necessary info.
    static struct PrintOptions {
        constexpr static bool titles               = 1;
        constexpr static bool attributes           = 1;
        constexpr static bool general_features     = 1;
        constexpr static bool core_features        = 1;
        constexpr static bool lib_features         = 1;
        constexpr static bool supported_features   = 1;
        constexpr static bool unsupported_features = 1;
        constexpr static bool sorted_by_value      = 0;
        constexpr static bool cxx11                = 1;
        constexpr static bool cxx14                = 1;
        constexpr static bool cxx17                = 1;
        constexpr static bool cxx20                = 1;
        constexpr static bool cxx23                = 0;
    }   print;
     
    struct CompilerFeature {
        CompilerFeature(const char* name = nullptr, const char* value = nullptr)
            : name(name), value(value) {}
        const char* name; const char* value;
    };
     
    static CompilerFeature cxx20[] = {
    COMPILER_FEATURE_ENTRY(__cpp_aggregate_paren_init)
    COMPILER_FEATURE_ENTRY(__cpp_char8_t)
    COMPILER_FEATURE_ENTRY(__cpp_concepts)
    COMPILER_FEATURE_ENTRY(__cpp_conditional_explicit)
    COMPILER_FEATURE_ENTRY(__cpp_consteval)
    COMPILER_FEATURE_ENTRY(__cpp_constexpr)
    COMPILER_FEATURE_ENTRY(__cpp_constexpr_dynamic_alloc)
    COMPILER_FEATURE_ENTRY(__cpp_constexpr_in_decltype)
    COMPILER_FEATURE_ENTRY(__cpp_constinit)
    COMPILER_FEATURE_ENTRY(__cpp_deduction_guides)
    COMPILER_FEATURE_ENTRY(__cpp_designated_initializers)
    COMPILER_FEATURE_ENTRY(__cpp_generic_lambdas)
    COMPILER_FEATURE_ENTRY(__cpp_impl_coroutine)
    COMPILER_FEATURE_ENTRY(__cpp_impl_destroying_delete)
    COMPILER_FEATURE_ENTRY(__cpp_impl_three_way_comparison)
    COMPILER_FEATURE_ENTRY(__cpp_init_captures)
    COMPILER_FEATURE_ENTRY(__cpp_modules)
    COMPILER_FEATURE_ENTRY(__cpp_nontype_template_args)
    COMPILER_FEATURE_ENTRY(__cpp_using_enum)
    };
    
    
    constexpr bool is_feature_supported(const CompilerFeature& x) {
        return x.value[0] != '_' && x.value[0] != '0' ;
    }
     
    inline void print_compiler_feature(const CompilerFeature& x) {
        constexpr static int max_name_length = 44; //< Update if necessary
        std::string value{ is_feature_supported(x) ? x.value : "------" };
        if (value.back() == 'L') value.pop_back(); //~ 201603L -> 201603
        // value.insert(4, 1, '-'); //~ 201603 -> 2016-03
        if ( (print.supported_features && is_feature_supported(x))
            || (print.unsupported_features && !is_feature_supported(x))) {
                std::cout << std::left << std::setw(max_name_length)
                          << x.name << " " << value << '\n';
        }
    }
     
    template<size_t N>
    inline void show(char const* title, CompilerFeature (&features)[N]) {
        if (print.titles) {
            std::cout << '\n' << std::left << title << '\n';
        }
        if (print.sorted_by_value) {
            std::sort(std::begin(features), std::end(features),
                [](CompilerFeature const& lhs, CompilerFeature const& rhs) {
                    return std::strcmp(lhs.value, rhs.value) < 0;
                });
        }
        for (const CompilerFeature& x : features) {
            print_compiler_feature(x);
        }
    }
     
    int main() {
        
        if (print.cxx20 && print.core_features) show("C++20 CORE", cxx20);
       
    }
    

    The following screenshots from the Compiler Explorer show the C++20 support of GCC 8.1, 10.1, and 14.1 compilers.

    • GCC 8.1
    • GCC 10.1
    • GCC 14.1

    What’s next?

    With my next post, I continue my journey through C + + 23.

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,