Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

New C++ features in GCC 12

April 25, 2022
Marek Polacek
Related topics:
Compilers
Related products:
Red Hat Enterprise Linux

Share:

    Version 12.1 of the GNU Compiler Collection (GCC) is expected to be released in April 2022. Like every major GCC release, this version will bring many additions, improvements, bug fixes, and new features. GCC 12 is already the system compiler in Fedora 36. GCC 12 will also be available on Red Hat Enterprise Linux in the Red Hat Developer Toolset (version 7) or the Red Hat GCC Toolset (version 8 and 9).

    Like the article I wrote about GCC 10, this article describes only new features affecting C++.

    We implemented several C++23 proposals in GCC 12. The default dialect in GCC 12 is -std=gnu++17; to enable C++23 features, use the -std=c++23 or -std=gnu++23 command-line options. (The latter option allows GNU extensions.)

    Note that C++20 and C++23 features are still experimental in GCC 12.

    C++23 features

    A number of previously prohibited constructs are now allowed, and some of these features can potentially reduce the size of your programs.

    if consteval

    C++17 introduced the constexpr if statement. The condition in if constexpr must be a constant expression (it is manifestly constant evaluated). If the condition evaluates to true, the else branch, if present, is discarded. Discarded means that the else branch is not instantiated at all during compilation, which is different behavior from an ordinary if. If the condition evaluates to false, the then branch is similarly discarded.

    If a function is declared constexpr, it might or might not be evaluated at compile time, depending on the context. To offer some visibility to the programmer about when the function is evaluated at compile time, C++20 introduced a new library function, std::is_constant_evaluated, which returns true if the current context is evaluated at compile time:

    #include <type_traits>
    
    int slow (int);
    
    constexpr int fn (int n)
    {
      if (std::is_constant_evaluated ())
        return n << 1; // #1
      else
        return slow (n); // #2
    }
    
    constexpr int i = fn (10); // does #1
    int n = 10;
    int i2 = fn (n); // calls slow function #2
    

    C++20 introduced the consteval keyword. A (possibly member) function or a constructor marked as consteval is an immediate function. Immediate functions are evaluated during compilation and must produce a constant, unless the call to an immediate function takes place in another immediate function; if they don't, the compiler produces an error. The compiler doesn't emit any actual code for such functions.

    However, the language rules do not allow the developer to replace n << 1 in the preceding test with a call to a consteval function:

    #include <type_traits>
    
    int slow (int);
    consteval int fast (int n) { return n << 1; }
    
    constexpr int fn (int n)
    {
      if (std::is_constant_evaluated ())
        return fast (n); // 'n' is not a constant expression
      else
        return slow (n);
    }
    constexpr int i = fn (10);
    

    To fix this problem, proposal P1938R3 introduced if consteval, which GCC 12 implements. if consteval allows the developer to invoke immediate functions, as shown here:

    #include <type_traits>
    
    int slow (int);
    consteval int fast (int n) { return n << 1; }
    
    constexpr int fn (int n)
    {
      if consteval {
        return fast (n); // OK
      } else {
        return slow (n);
      }
    }
    
    constexpr int i = fn (10);
    

    Note that it is valid to have if consteval in an ordinary, non-constexpr function. Also note that if consteval requires { }, unlike the ordinary if statement.

    There is a problem with the interaction between if constexpr and std::is_constant_evaluated, but fortunately the compiler can detect that problem. We'll examine the solution in the later section Extended std::is_constant_evaluated in if warning.

    auto(x)

    GCC 12 implements proposal P0849, which allows auto in a function-style cast, the result of which is a pure rvalue (prvalue):

    struct A {};
    void f(A&);  // #1
    void f(A&&); // #2
    A& g();
    
    void
    h()
    {
      f(g()); // calls #1
      f(auto(g())); // calls #2 with a temporary object
    }
    

    Note that both auto(x) and auto{x} are accepted; however, decltype(auto)(x) remains invalid.

    Non-literal variables in constexpr functions

    GCC 12 implements C++23 proposal P2242R3, which allows non-literal variables, gotos, and labels in constexpr functions so long as they are not constant evaluated. This expanded behavior is useful for code like the following (taken from the proposal):

    #include <type_traits>
    
    template<typename T> constexpr bool f() {
      if (std::is_constant_evaluated()) {
        return true;
      } else {
        T t; // OK when T=nonliteral in C++23
        return true;
      }
    }
    struct nonliteral { nonliteral(); };
    static_assert(f<nonliteral>());
    

    This does not compile in C++20, but compiles in C++23 because the else branch is not evaluated. The following example also compiles only in C++23:

    constexpr int
    foo (int i)
    {
      if (i == 0)
        return 42;
      static int a;
      thread_local int t;
      goto label;
    label:
      return 0;
    }
    

    Multidimensional subscript operator

    GCC 12 supports C++23 proposal P2128R6, a multidimensional subscript operator. Comma expressions within subscripting expressions were deprecated in C++20 via proposal P1161R3, and in C++23 the comma in [ ] has changed meaning.

    C++ uses the operator[] member function to access the elements of an array, as well as array-like types such as std::array, std::span, std::vector, and std::string. However, this operator did not accept multiple arguments in C++20, so access to the elements of multidimensional arrays was implemented using function call operators like arr(x, y, z), and similar workarounds. These workarounds have a number of drawbacks, so to alleviate the issues when using them, C++23 allows operator[] to take zero or more arguments.

    As a consequence, this test case is accepted with -std=c++23:

    template <typename... T>
    struct W {
      constexpr auto operator[](T&&...);
    };
    
    W<> w1;
    W<int> w2;
    W<int, int> w3;
    

    Here is what may be a clearer example, with a very naive implementation:

    struct S {
      int a[64];
      constexpr S () : a {} {};
      constexpr S (int x, int y, int z) : a {x, y, z} {};
      constexpr int &operator[] () { return a[0]; }
      constexpr int &operator[] (int x) { return a[x]; }
      constexpr int &operator[] (int x, long y) { return a[x + y * 8]; }
    };
    
    void g ()
    {
      S s;
      s[] = 42;
      s[5] = 36;
      s[3, 4] = 72;
    }
    

    As an extension, GCC still supports the old behavior when an overloaded subscript operator is not found, though it issues a warning:

    void f(int a[], int b, int c)
    {
      a[b,c]; // deprecated in C++20, invalid but accepted with a warning in C++23
      a[(b,c)]; // OK in both C++20 and C++23
     }
    

    Note that currently, operator[] does not support default arguments. It appears, though, that default arguments will be allowed in future versions: see CWG 2507. If and when the proposed adjustment is accepted, the following example will be allowed:

    struct X {
      int a[64];
      constexpr int& operator[](int i = 1) { return a[i]; }
    };
    

    elifdef and elifndef

    In C and C++, the #ifdef and #ifndef preprocessing directives are "syntactic sugar" for #if defined(something) and #if !defined(something). Surprisingly, the else variants of these directives did not have the same shorthands. To amend this omission, the C and C++ designers accepted proposals N2645 and P2334R1, respectively. GCC 12 implements both proposals, so the following example compiles correctly:

    #ifdef __STDC__
    /* ... */
    #elifndef __cplusplus
    #warning "not ISO C"
    #else
    /* ... */
    #endif
    

    Please note that, to compile this example without errors in C++20 and earlier, you must enable GNU extensions. In other words, -std=c++20 causes a compile error, but -std=gnu++20 causes only a pedantic warning if -Wpedantic is also turned on.

    Extended init-statement

    GCC 12 implements proposal P2360R0 in C++23, which merely extends an init-statement (used in if, for, and switch statements) to allow it to contain an alias-declaration. In practice, that change means that the following code is accepted:

    for (using T = int; T e : v)
      {
        // use e
      }
    

    Corrections and internal improvements

    The changes described in this section bring GCC more in line with recent changes to the standard, and permit behavior that previously did not work correctly.

    Dependent operator lookup changes

    GCC 12 corrected a problem where the compiler performed an unqualified lookup for a dependent operator expression at template definition time instead of at instantiation time. The fix matches the existing behavior for dependent call expressions. Consider the following test case demonstrating this change:

    #include <iostream>
    
    namespace N {
      struct A { };
    }
    
    void operator+(N::A, double) {
      std::cout << "#1 ";
    }
    
    template<class T>
    void f(T t) {
      operator+(t, 0);
      t + 0;
    }
    
    // Since it's not visible from the template definition, this later-declared
    // operator overload should not be considered when instantiating f<N::A>(N::A),
    // for either the call or operator expression.
    void operator+(N::A, int) {
      std::cout << "#2 ";
    }
    
    int main() {
      N::A a;
      f(a);
      std::cout << std::endl;
    }
    

    This program will print #1 #2 when compiled with versions 11 or older of GCC, but GCC 12 correctly prints #1 #1. That's because previously only the call expression resolved to the #1 overload, but with GCC 12 the operator expression does too.

    auto specifier for pointers and references to arrays

    GCC 12 also supports defect report DR2397, covering the auto specifier for pointers and references to arrays. This change removes the restriction that the array element type may not be a placeholder type. This allows code like:

    int a[3];
    auto (*p)[3] = &a;
    auto (&r)[3] = a;
    

    However, neither of the following examples work (although some day they might):

    auto (&&r)[2] = { 1, 2 };
    auto arr[2] = { 1, 2 };
    
    int arr[5];
    auto x[5] = arr;
    

    These don't work because the auto deduction is performed in terms of function template argument deduction, so the array decays to a pointer.

    Folding of trivial functions

    A well-formed call to std::move or std::forward is equivalent to a cast. But because these constructs are implemented as function calls, the compiler generates debugging information that persists even after the call gets inlined. This extra code is a waste because there's no need to debug such operations. Therefore, GCC 12 elides calls to certain trivial inline functions (such as std::move, std::forward, std::addressof, and std::as_const) into simple casts as part of the front end's general expression folding routine. As a result, the debugging information produced by GCC could be up to 10 percent smaller, while improving GCC's compile time and memory usage. This behavior is controlled by a new option called -ffold-simple-inlines.

    Fixing the overly permissive specification of enum direct-list-initialization

    GCC 12 implements defect report DR2374, which forbids, for instance, direct-list-initialization of a scoped enumeration from a different scoped enumeration:

    enum class Orange;
    enum class Apple;
    Orange o;
    Apple a{o}; // error with GCC 12
    

    Restrictions on non-type template arguments in partial specializations

    Previously, an overly strict restriction prevented certain uses of template parameters as template arguments. This restriction has been rectified in response to defect report DR1315, and GCC implements these uses now. Therefore, the following plausible use of a template parameter as a template argument compiles correctly:

    template <int I, int J> struct A {};
    template <int I> struct A<I, I*2> {}; // OK with GCC 12
    

    Substitutions into function parameters in lexical order

    C++ template argument deduction underwent some changes when defect report DR1227 specified that the substitution proceeds in lexical order—that is, from left to right. The following code demonstrates what effect this might have:

    template <typename T>
    struct A { using type = typename T::type; };
    
    template <typename T> void g(T, typename A<T>::type);
    template <typename T> long g(...);
    
    long y = g<void>(0, 0); // OK in GCC 12, error in GCC 11
    
    template <class T> void h(typename A<T>::type, T);
    template <class T> long h(...);
    
    long z = h<void>(0, 0); // error in GCC 12, OK in GCC 11
    

    GCC 12 substitutes the arguments in left-to-right order and checks whether a substituted type is erroneous before substituting it into the rest of the arguments. Thus, for g<void>(0, 0) the compiler tries to substitute void into g(T, typename A<T>::type) and sees that the first substitution results in an invalid parameter type void. This invalid substitution is a SFINAE failure, so the first overload is discarded and the g(...) one is chosen instead. However, for h<void>(0, 0), the compiler first substitutes void into the typename A<T>::type parameter. This produces a hard error, because instantiating A<void> is not an immediate context.

    GCC 11 and earlier performed the substitution in right-to-left order, so the situation was reversed: g<void>(0, 0) resulted in a compile error, whereas h<void>(0, 0) compiled fine.

    Stricter checking of attributes on friend declarations

    If a friend declaration has an attribute, that declaration must be a definition, but before version 12, GCC didn't check this restriction. Moreover, a C++11 attribute cannot appear in the middle of the decl-specifier-seq:

    template<typename T>
    struct S {
      [[deprecated]] friend T; // warning: attribute ignored
      [[deprecated]] friend void f(); // warning: attribute ignored
      friend [[deprecated]] int f2(); // error
    };
    S<int> s;
    

    Deduction guides can be declared at class scope

    Due to a bug, deduction guides could not be declared at class scope in versions 11 and early of GCC. This has been fixed in GCC 12, so the following test case compiles correctly:

    struct X {
      template<typename T> struct A {};
      A() -> A<int>;
    };
    

    Class-scope non-template deduction guides are now supported as well in GCC 12.

    Ordered comparison of null pointers is now rejected

    Relational comparisons between null pointer constants and pointers are ill-formed, and this error is diagnosed in GCC 12:

    decltype(nullptr) foo ();
    auto cmp = foo () > 0; // error: ordered comparison of pointer with integer zero
    

    The overall defect resolution status is listed in the C++ Defect Report Support in GCC page.

    New and improved warnings

    GCC's plethora of warning options have been enhanced in GCC 12.

    -Wuninitialized extended

    The -Wuninitialized warning has been extended to warn about using uninitialized variables in member initializer lists. Therefore, the front end can detect bugs like this:

    struct A {
      int a;
      int b;
      A() : b(1), a(b) { }
    };
    

    Here, the b field is used uninitialized because the order of member initializers in the member initializer list is irrelevant; what matters is the order of declarations in the class definition. (A related warning, -Wreorder, can be used to warn when the order of member initializers does not match the declaration order.)

    The warning does not warn about more complex initializers. And it also does not warn when the address of an object is used:

    struct B {
      int &r;
      int *p;
      int a;
      B() : r(a), p(&a), a(1) { } // no warning
    };
    

    As an aside, the request to enhance this warning came about 17 years ago. Apparently, sometimes things take time.

    -Wbidi-chars added

    The -Wbidi-chars warning warns about potentially misleading UTF-8 bidirectional control characters, which can change left-to-right writing direction into right-to-left (and vice versa). Certain combinations of control characters might cause confusion for the programmer because code that has seemingly been commented out might actually be compiled, or vice versa. This warning is supposed to mitigate CVE-2021-42574, aka Trojan Source.

    For more information, please refer to David Malcolm's Red Hat Developer article Prevent Trojan Source attacks with GCC 12.

    -Warray-compare added

    The new -Warray-compare option warns about comparisons between two operands of array type, which was deprecated in C++20. Here's an example of this situation:

    int arr1[5];
    int arr2[5];
    bool same = arr1 == arr2; // warning: comparison between two arrays
    

    -Wattributes extended

    The -Wattributes warning has been extended so that developers can now use -Wno-attributes=ns::attr or -Wno-attributes=ns:: to suppress warnings about unknown scoped attributes (in C++11 and C2X). Similarly, #pragma GCC diagnostic ignored_attributes "ns::attr" can be used to achieve the same effect. The new behavior is meant to help with vendor-specific attributes, where a warning is not desirable, while still detecting typos. Consider:

    [[deprecate]] void g(); // warning: should be deprecated
    [[company::attr]] void f(); // no warning
    

    When compiled with -Wno-attributes=company::, only the first declaration issues a warning.

    New warning options for C++ language mismatches

    GCC 12 gained the following new warning options, enabled by default:

    • -Wc++11-extensions
    • -Wc++14-extensions
    • -Wc++17-extensions
    • -Wc++20-extensions
    • -Wc++23-extensions

    These options control existing pedantic warnings about occurrences of new C++ constructs in code that uses an older C++ standard dialect. For instance, developers are now able to suppress warnings when using variadic templates in C++98 code by applying the new -Wno-c++11-extensions option.

    Extended std::is_constant_evaluated in if warning

    Because the condition in if constexpr is manifestly constant evaluated, if constexpr (std::is_constant_evaluated()) is always evaluated to be true. GCC 10 introduced a warning to detect this bug, and GCC 12 extended the warning to detect more dubious cases. For instance:

    #include <type_traits>
    
    int
    foo ()
    {
      if (std::is_constant_evaluated ()) // warning: always evaluates to false in a non-constexpr function
        return 1;
      return 0;
    }
    
    consteval int
    baz ()
    {
      if (std::is_constant_evaluated ()) // warning: always evaluates to true in a consteval function
        return 1;
      return 0;
    }
    

    -Wmissing-requires added

    The -Wmissing-requires option warns about a missing requires. Consider the following code:

    template <typename T> concept Foo = __is_same(T, int);
    
    template<typename Seq>
    concept Sequence = requires (Seq s) {
      /* requires */ Foo<Seq>;
    };
    

    The problem here is that the developer presumably meant to invoke the Foo concept (a nested requirement), which needs to be prefixed by the requires keyword. In this test, Foo<Seq> is a concept-id, which makes Sequence true if Foo<Seq> is a valid expression. The expression is valid for all Seq.

    -Waddress enhanced

    The -Waddress warning has been extended. It now warns about, for instance, comparing the address of a nonstatic member function to the null pointer value:

    struct S {
      void f();
    };
    
    int g()
    {
      if (&S::f == nullptr) // warning: the address &S::f will never be NULL
        return -1;
      return 0;
    }
    

    Acknowledgments

    As usual, I'd like to thank my coworkers at Red Hat who made the GNU C++ compiler so much better, notably Jason Merrill, Jakub Jelinek, Patrick Palka, and Jonathan Wakely.

    Conclusion

    In GCC 13, we plan to finish up the remaining C++23 features. For progress so far, see the C++23 Language Features table on the C++ Standards Support in GCC page. Please do not hesitate to file bug reports in the meantime and help us make GCC even better.

    Last updated: October 8, 2024

    Related Posts

    • Porting your code to C++17 with GCC 11

    • Use source-level annotations to help GCC detect buffer overflows

    • Prevent Trojan Source attacks with GCC 12

    • How to install GCC 8 and Clang/LLVM 6 on Red Hat Enterprise Linux 7

    • Recommended compiler and linker flags for GCC

    Recent Posts

    • How to run AI models in cloud development environments

    • How Trilio secures OpenShift virtual machines and containers

    • How to implement observability with Node.js and Llama Stack

    • How to encrypt RHEL images for Azure confidential VMs

    • How to manage RHEL virtual machines with Podman Desktop

    What’s up next?

    systemd Commands cheat sheet card image

    Download the systemd Commands cheat sheet for all the command-line executables you need to work with systemd, a service that runs on Linux to consolidate service configuration and application behavior. 

    Get the cheat sheet
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue