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 14

May 15, 2024
Marek Polacek
Related topics:
CompilersLinux
Related products:
Red Hat Enterprise Linux

Share:

    The next major version of the GNU Compiler Collection (GCC), 14.1, was released on May 7 2024. Like every major GCC release, this version brings many additions, improvements, bug fixes, and new features. GCC 14 is already the system compiler in Fedora 40. Red Hat Enterprise Linux (RHEL) users will get GCC 14 in the Red Hat GCC Toolset (RHEL 8 and RHEL 9), and as the system compiler in RHEL 10. It's also possible to try GCC 14 on Compiler Explorer and similar pages.

    Like the article I wrote about GCC 13, this article describes only new features implemented in the C++ front end; it does not discuss developments in the C++ language itself.

    The default dialect in GCC 14 is still -std=gnu++17. You can use the -std=c++23 or -std=gnu++23 command-line options to enable C++23 features, and similarly for C++26 and others. Note that C++20, C++23, and C++26 features are still experimental in GCC 14.

    C++26 features

    Trivial infinite loops and UB

    This proposal brings C and C++ a little closer together. The C++ standard, unlike the C standard, defined forward progress in such a way that it assumed that even trivial infinite loops will eventually terminate; if they didn’t, the program invoked undefined behavior. This is a problem if you write very low-level code where such infinite loops are common. The proposal added a definition of a trivially empty iteration statement, whereby a loop whose controlling expression is a constant expression that evaluates to true is a trivial infinite loop; these are now included in the definition of forward progress. For example, in GCC 14, the following snippet no longer causes undefined behavior (in all C++ dialects, not only C++26):

    void
    g ()
    {
      while (true)
    /* do something */;
    }

    Static storage for braced initializers

    This proposal, implemented in GCC 14, allows the compiler to better optimize code using std::initializer_list. The implementation of std::initializer_list uses a backing array for its data. Prior to this proposal, the standard didn’t allow the compiler to allocate the backing array in static storage even when the contents were constant data because all backing arrays had to be distinct. So the compiler had no other choice than to copy the data from read-only memory onto the stack every time the list was used. The requirement is no longer mandated by the standard, therefore the backing arrays can be shared (which was already the case for string-literals). In short, GCC 14 optimizes away the memcpy calls copying the backing array in the following snippet.

    #include <initializer_list>
    void f(std::initializer_list<int> il);
    void g() {
       f({3,2,1, 0, 1, 2, 3}); // array in .rodata
    }

    Unevaluated strings

    GCC 14, in C++26 mode, implements P2361R6 which adds the notion of unevaluated strings. Unevaluated strings appear in context such as _Pragma, static_assert, or the [[nodiscard]] and [[deprecated]] attributes. These strings are not used in the program at runtime, so they are not converted to the execution encoding like evaluated strings. Since they aren’t going to be converted to a different string encoding, they cannot contain numeric escape sequences, and they cannot have any prefixes. In practice, this means that the following two lines will cause diagnostics to be emitted in C++26 mode:

    static_assert (true, u"foo"); // error in C++26
    static_assert (true, "\x{20}"); // warning in C++26 in pedantic mode

    Constexpr cast from void*

    GCC 14 implements P2738R1, so the following test is accepted in C++26. This feature allows users to perform various type erasure tricks in constexpr code. (In C++23 and earlier, the compiler still emits an error about trying to cast from void* in a constant expression.)

    struct A { int i; };
    struct B { A a; };
    constexpr int f()
    {
      B b { 42 };
      void *p = &b;
      A* ap = static_cast<A*>(p); // OK
      return ap->i;
    }
    static_assert (f() == 42);

    User-generated static_assert messages

    With this proposal implemented in GCC 14, the user is allowed to use static_assert more generally as it now also accepts user-generated messages (which are manifestly constant evaluated). For example:

    struct error {
      constexpr int size () const { return sizeof "hello" - 1; }
      constexpr const char *data () const { return "hello"; }
    };
    static_assert (false, error{});

    Gives the following error message:

    error: static assertion failed: hello

    Placeholder variables

    GCC 14 implements proposal P2169R4. This proposal allows users to use the special _ variable, which serves as a placeholder for an unnamed variable which will not produce the “unused variable” diagnostic even when it’s not used. _ can also redefine an existing declaration in the current scope (but actually using _ would trigger an error if more than one variable has been declared with that name):

    X foo ();
    void
    g ()
    {
      auto _ = foo ();
      auto _ = 42;       // OK
      int i = _;         // error: reference to ‘_’ is ambiguous
    }

    Returning reference to temporary

    P2748R5 makes it ill-formed to return a reference to temporary. GCC has warned about such dangerous code for a long time, but C++26 makes it an error.

    auto&&
    foo ()
    {
      return 42;  // ill-formed in C++26
    }

    C++23 features

    Deducing this

    GCC 14 implemented this proposal, which a lot of users clamored for. It allows users to add an explicit this parameter to non-static member functions; this is called an explicit object parameter. That way, you can deduce the type and value category of the class object. This is useful to avoid code duplication; previously, the developer was forced to write several member function overloads: non-const, const, and with ref-qualifiers even const & and && (this ignores volatile and similar frills). With this proposal, instead of:

    class S {
      int value;
    public:
      int get_add (int i) & { return value + i; }
      int get_add (int i) && { return value + i; }
      int get_add (int i) const & { return value + i; }
    };
    auto g (S s, const S& sr) -> int
    {
      auto i1 = s.get_add (42);
      auto i2 = sr.get_add (42);
      auto i3 = S{}.get_add (42);
      return i1 + i2 + i3;
    }

    You can simplify the code to:

    class S {
      int value;
    public:
      template<typename Self>
      int get_add (this Self&& self, int i) { return self.value + i; }
    };
    S s{};
    s.get_add(1);                // Self is deduced as S&
    std::move(s).get_add(2);     // Self is deduced as S
    std::as_const(s).get_add(3); // Self is deduced as const S&

    Deducing this also allows you to write recursive lambdas. For example:

    int
    main ()
    {
      auto fib = [](this auto self, int n) {
        if (n < 2)
          return n;
        return self (n - 1) + self (n - 2);
      };
      static_assert (fib (6) == 8);
    }

    There are many more examples where the explicit object parameter can be useful. Naturally, it has some restrictions; for example, an explicit object member function cannot be static or virtual.

    References in constant expressions

    A modern C++ way to implement a function to get the size of an array can be:

    template<typename T, size_t N>
    constexpr auto array_size (T (&)[N]) -> size_t {
      return N;
    }

    This function can be used as in this code example:

    void g ()
    {
      int arr[] = { 1, 3, 3, 7 };
      constexpr auto sz = array_size (arr);
    }

    The code works without any surprises. When the array in question was a parameter, however, as in this example:

    void g (const int (&arr)[3])
    {
      constexpr auto sz = array_size (arr);
    }

    The compiler, due to some fairly arcane rules, rejected the code with error: ‘arr’ is not a constant expression . The rules have been adjusted and the code works as expected with GCC 14 (in all C++ dialects).

    consteval needs to propagate up

    Implementing this proposal in GCC 14 turned out to be somewhat knotty. It causes certain functions to be promoted to consteval, that is, making them immediate functions. Consider:

    consteval int id(int i) { return i; }
    template <typename T>
    constexpr int f(T t)
    {
      return t + id(t); // id causes f<int> to be promoted to consteval
    }
    void g(int i)
    {
      f (3);
    }

    Previously the code was invalid and therefore the compiler gave an error saying that t in f is not a valid constant expression. With this proposal, f gets promoted to consteval, which means that the call id(t) is in an immediate context. So the call does not need to be a constant expression. (This is how consteval function composition works – one consteval function calling another one.) Note that making f<int> consteval means that the f(3) call must be a valid constant expression.

    The standard describes which expressions cause their enclosing function to be promoted to consteval; these expressions are called immediate-escalating. For example, a call to an immediate (that is, consteval) function that is not a constant expression and is not a subexpression of an immediate invocation is an immediate-escalating expression. Not every function can be promoted to consteval. The standard says that only immediate-escalation functions can be promoted. An example of an immediate-escalating function is a template function declared as constexpr.

    The behavior described in the proposal is enabled by default in C++20 mode or later, but you can suppress function escalation with the -fno-immediate-escalation command-line option.

    CTAD from inherited constructors

    GCC 14 supports P2582R1, a proposal which extends CTAD (Class Template Argument Deduction) for inherited constructors. Consequently, the following testcase compiles in C++23 mode:

    template <typename T> struct B {
    B(T);
    };
    template <typename T> struct C : B<T*> {
    using B<T*>::B;
    };
    
    int* p;
    C c(p); // OK, deduces C<int> 

    Defect report resolutions

    GCC 14 implements a number of defect report resolutions, which are usually applied for all affected -std dialects, not only the newest standards. Please see our C++ DR table for a more detailed status.

    stricter constinit

    CWG issue 2543 clarifies that constinit variables with non-constant initializers should be diagnosed, even if the variable could be initialized statically. 

    goto and trivially-initialized objects

    Since CWG Issue 2256, goto can cross the initialization of a trivially initialized object with a non-trivial destructor:

    struct A {
      ~A() { }
    };
    void f()
    {
      goto L;
      A a;
    L:
      return;
    } 

    List-initialization and conversions in overload resolution

    CWG 1228 reaffirmed the difference between copy-initialization and copy-list-initialization with regard to explicit candidates: in the context of copy-initialization, only converting (non-explicit) candidates are considered; in the context of copy-list-initialization, explicit candidates are considered, but it is an error if an explicit candidate is actually selected. We implemented this change as part of the PR109159 fix, but it caused issues in practice. The problem was mitigated by CWG 2735 which GCC 14 and GCC 13 implement. Consequently, the test from CWG 2735 compiles again:

    struct Z {};
    struct X {
      explicit X();         // #1
      explicit X(const Z &z);  // #2
    };
    struct Y {
      Y() : x({}) {}  // OK, calls #2, not ambiguous with the copy constructor of X using a temporary initialized by #1
      X x;
    };

    DR 2237 improved

    DR 2237 prohibited using a template-id as a constructor or destructor in C++20. GCC 11 implemented this change, but it was too draconic: it issued a hard error and the unclear message hardly brought any joy. GCC 14 downgrades the error to a new -Wtemplate-id-cdtor warning, and better explains what the problem is.

    template<typename T>
    struct X {
      X<T>();  // template-id not allowed for constructor
      ~X<T>(); // template-id not allowed for destructor
    };

    Additional updates

    More checking in templates

    In GCC 14, non-dependent simple assignments are checked even in templates; that is, some invalid code is checked even when the template has not been instantiated. For example:

    int n;
    template <int>
    void g()
    {
      -n = 0;  // error
    }

    In-class variable template partial specializations

    In-class variable template partial specializations are now accepted by the compiler; previously, these were wrongly rejected:

    struct A {
      template<class T> static const int var = 0;
      template<class T> static const int var<T*> = 1;
      template<class T> static const int var<const T*> = 2;
    };
    static_assert(A::var<int> == 0);
    static_assert(A::var<int*> == 1);
    static_assert(A::var<const int*> == 2); 

    constexpr lifetime tracking

    GCC 14 implements constexpr lifetime tracking, which allows the compiler to detect using objects after their lifetime has ended while evaluating code at compile time.

    struct S {
      int x = 0;
      constexpr const int& get() const { return x; }
    };
    constexpr const int& test() {
      auto local = S{};
      return local.get();
    }
    constexpr int x = test();  // error: accessing local outside its lifetime

    NRVO extended

    In GCC 14, the named return value optimization (NRVO) is performed even for variables declared in an inner block of a function:

    struct A {
      int i;
      A(int i) : i(i) { }
      A(const A &); // not defined
    };
    A h() {
      {
        A a(0);
        return a;
      }
    }
    int main () {
      A c = h(); // A::A(const A &) not called
    }

    hot/cold attribute on types

    The hot and cold attributes can now be applied to classes as well. The effect of the attribute will be propagated to the class’s members:

    struct __attribute__((cold)) A {
       void feet () { } // feet is cold
    };

    You can visit the manual for more information about this attribute.

    New and improved warnings

    -Wnrvo added

    Relatedly to the NRVO extension described above, GCC has a new warning that warns when the compiler didn’t elide the copy from a local variable to the return value of a function in a context where it is allowed by the standard (see [class.copy.elision] in the C++ standard for details). For instance, the compiler warns here:

    struct A {
      A() {}
      A(const A&);
    };
    A test() {
      A a, b;
      if (true)
        return a;
      else
        return b; // warning: not eliding copy on return in 'A test()'
    }

    This is because it can’t elide from both a and b.

    -Wdangling-reference false positives reduced

    GCC 13 introduced the -Wdangling-reference warning, which may detect bugs in the code when a reference to a destroyed object is used. Unfortunately, it had a lot of false positives, raising the users’ ire. Most of the shortcomings were fixed in GCC 14; for example, the warning no longer triggers on std::span-like classes. We consider a non-union class that has a pointer data member and a trivial destructor std::span-like. Moreover, if the heuristic still falls short, the users can now use the [[gnu::no_dangling]] attribute to suppress the warning. See -Wdangling-reference documentation for more information.

    New option to show considered candidates

    GCC 14 added the -fdiagnostics-all-candidates option which tells the compiler to show all function candidates it considered when overload resolution failure occurs. 

    Improved diagnostic for explicit conversion functions

    When a conversion function could not be chosen only because it was marked explicit, the compiler now provides a better explanation, rather than just saying that the conversion was invalid:

    struct S {
      explicit S(int) { }
      explicit operator bool() const { return true; } // note: explicit conversion function was not considered
    };
    void
    g ()
    {
      bool b = S{1}; // error: cannot convert ‘S’ to ‘bool’ in initialization
    }

    Improved "not a constant expression" diagnostic

    When taking the address of a non-static constexpr variable, the compiler now better explains why that’s a problem. For instance, here:

    void test ()
    {
      constexpr int i = 42;
      constexpr const int *p = &i;
    }

    The compiler says “address of non-static constexpr variable ‘i’ may differ on each invocation of the enclosing function; add ‘static’ to give it a constant address”.

    Modules

    C++ modules remain experimental in GCC 14, but dozens of bugs have been fixed in this release.

    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. Also thanks to Nathaniel Shead, who has tackled many pesky C++ modules bugs, and waffl3x, who worked on the Deducing This proposal.

    Last updated: December 12, 2024

    Related Posts

    • Migrating C and C++ applications from Red Hat Enterprise Linux version 7 to version 8

    • Extend C++ capabilities with LLVM STLExtras.h

    • Static analysis in GCC 10

    • Understanding GCC warnings

    • C# 12: Collection expressions and primary constructors

    Recent Posts

    • LLM Compressor: Optimize LLMs for low-latency deployments

    • How to set up NVIDIA NIM on Red Hat OpenShift AI

    • Leveraging Ansible Event-Driven Automation for Automatic CPU Scaling in OpenShift Virtualization

    • Python packaging for RHEL 9 & 10 using pyproject RPM macros

    • Kafka Monthly Digest: April 2025

    What’s up next?

    Download the .NET on Red Hat Enterprise Linux cheat sheet, which covers the basics of installation, how to get a simple program running, and how to run a program in a Linux container.

    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

    OSZAR »