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 15

April 24, 2025
Marek Polacek
Related topics:
CompilersLinux
Related products:
Red Hat Enterprise Linux

Share:

    The next major version of the GNU Compiler Collection (GCC), 15.1, is expected to be released in April or May 2025. Like every major GCC release, this version will bring many additions, improvements, bug fixes, and new features. GCC 15 is already the system compiler in Fedora 42. Red Hat Enterprise Linux (RHEL) users will get GCC 15 in the Red Hat GCC Toolset. It's also possible to try GCC 15 on Compiler Explorer and similar pages.

    Like my previous article, New C++ features in GCC 14, 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 15 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. 

    Warning

    Note that C++20, C++23, and C++26 features are still experimental in GCC 15. (GCC 16 plans to switch the default to C++20.)

    C++26 features

    C++26 features in GCC 15 include pack indexing, attributes for structured bindings, enhanced support for functions whose definition consists of =delete, and more.

    Pack indexing

    C++11 introduced variadic templates which allow the programmer to write templates that accept any number of template arguments. A pack can represent a series of types or values. For example, to print out arbitrary arguments, one could write:

    template<typename T, typename... Types>
    void print (T t, Types... args)
    {
      std::cout << t << '\n';
      if constexpr (sizeof...(args) > 0)
        print (args...);
    }
    
    int main ()
    {
      print ('a', "foo", 42);
    }

    However, it was not possible to index an element of a pack, unless the programmer resorted to using various recursive tricks which are generally slow to compile. With this C++26 feature, to index a pack one can write pack...[N] (where N has to be a constant expression). A pack index then behaves exactly as if the resulting expression was used. An empty pack cannot be indexed. The following program will print a:

    template<typename... Types>
    void print (Types... args)
    {
      std::cout << args...[0] << '\n';
    }
    
    int main ()
    {
      print ('a', "foo", 42);
    }

    Attributes for structured bindings

    This proposal allows you to add an attribute that appertains to each of the introduced structured bindings, as in the following example:

    struct S { int a, b; };
    
    void
    g (S& s)
    {
      auto [ a, b [[gnu::deprecated]] ] = s;
    }

    =delete with a reason

    C++11 provided support for deleted functions; that is, functions whose definition consists of =delete. Deleted functions participate in overload resolution, but calling them is an error. This replaced the old mechanism of declaring special member functions as private. 

    In C++26, it is possible to provide a message explaining why the function is marked as deleted: =delete(“reason”). The following program:

    void oldfn(char *) = delete("unsafe, use newfn");
    void newfn(char *);
    
    void g ()
    {
      oldfn ("bagel");
    }

    will cause the compiler to emit:

    q.C: In function ‘void g()’:
    q.C:7:9: error: use of deleted function ‘void oldfn(char*)’: unsafe, use newfn
    7 |   oldfn ("bagel");
      |   ~~~~~~^~~~~~~~~
    q.C:1:6: note: declared here
    1 | void oldfn(char *) = delete("unsafe, use newfn");
      |      ^~~~~

    Variadic friends

    The following feature makes it possible to use a friend declaration with a parameter pack:

    template<class... Ts>
    class Foo {
      friend Ts...;
    };

    An example where this feature can be used in practice is the Passkey idiom:

    template<typename... Ts>
    class Passkey {
      friend Ts...;
      Passkey() {}
    };
    
    class A;
    class B;
    
    struct Widget {
      // Only callable from A and B.
      void secret (Passkey<A, B>);
    };
    
    class A {
      void doit (Widget& w) {
        w.secret ({}); // OK
      }
    };
    
    class B {
      void doit (Widget& w) {
        w.secret ({}); // OK
      }
    };
    
    class D {
      void doit (Widget& w) {
        w.secret ({}); // won't compile!
      }
    };

    constexpr placement new

    C++20 added support for using new in a constexpr context:

    
    constexpr void use (int *) { }
    
    constexpr int
    foo ()
    {
      auto *p = new int[]{1, 2, 3};
      use (p);
      delete[] p;
      return 1;
    }
    
    int main ()
    {
      constexpr auto r = foo ();
    }

    GCC implemented the proposal in GCC 10. However, constexpr placement new was not yet possible. C++26 rectifies that situation, and it allows the programmer to write code like:

    #include <memory>
    
    constexpr int foo ()
    {
      std::allocator<int> alloc;
      auto p = alloc.allocate (16);
      new (p) int(42);
      alloc.deallocate (p, 16);
      return 1;
    }
    
    int main ()
    {
      constexpr int r = foo ();
    }

    Structured binding declaration as a condition

    The structured bindings feature was added in C++17, and GCC has supported it for a long time. GCC 15 implements P0963R3, which allows structured bindings declaration in if/while/for/switch conditions; previously, this wasn’t possible. 

    If a structured binding is used in a condition context, its decision variable is the underlying artificial variable created by the compiler. This variable must be convertible to bool (except when used in a switch statement). For example:

    struct S {
      int a, b;
      explicit operator bool () const noexcept { return a != b; }
    };
    void use (int, int);
    
    void g (S s)
    {
      if (auto [ a, b ] = s)
        use (a, b);
    }

    In the preceding example, use will be called when a and b, decomposed from s, are not equal. The artificial variable used as the decision variable has a unique name and its type here is S.

    Deleting a pointer to an incomplete type

    C++26 made it clear that delete and delete[] on a pointer to an incomplete class type is invalid. Previously it invoked undefined behavior unless the class had a trivial destructor and no custom deallocator. Consequently, GCC 15 will emit an error in C++26 mode, or a warning in older modes for this example:

    struct S;
    
    void foo (S *p)
    {
      delete p;
    }

    The Oxford variadic comma

    This paper deprecates the use of a variadic ellipsis without a preceding comma in C++26. That means that GCC 15, when compiling the following test case from the proposal, will emit three warnings in C++26 mode:

    void d_depr(auto......); // deprecated
    void d_okay(auto..., ...);  // OK
    
    void g_depr(int...);     // deprecated
    void g_okay(int, ...);   // OK
    
    void h_depr(int x...);   // deprecated
    void h_okay(int x, ...); // OK

    Users can enable the warning in older modes by specifying -Wdeprecated-variadic-comma-omission.

    Remove deprecated array comparison

    This paper makes the following program comparing two arrays ill-formed:

    int arr1[5];
    int arr2[5];
    bool same = arr1 == arr2;

    The comparison was deprecated in C++20, but GCC 14 emitted a warning only when -Wall was specified. GCC 15 emits an error in C++26, and a warning in older modes even without -Wall.

    #embed

    This paper, first proposed for C23, has made its way into C++26. It gives the programmer a new directive to include binary data into the program. 

    A dedicated article discusses this feature in detail: How to implement C23 #embed in GCC 15

    Defect report resolutions

    A number of defect reports were resolved in GCC 15. A few examples follow. The overall status can be viewed here.

    Redeclaration of using-declarations

    DR 36 clarifies that it’s valid to redeclare an entity via a using-declaration if the redeclaration happens outside a class scope. As a result, the following program compiles with GCC 15. GCC 14 would report a “redeclaration” error.

    enum class E { Smashing, Pumpkins };
    void foo() {
      using E::Smashing;
      using E::Smashing;
      using E::Pumpkins;
    }

    Trivial fixes

    GCC 15 fixes DR 1363 and DR 1496. Certain classes that were previously considered trivial are not trivial anymore. The details can be found in this commit message.

    Bit-fields and narrowing conversions

    The resolution of DR 2627 means that GCC 15 no longer emits the narrowing conversion warning here:

    #include <compare>
    
    struct C {
      long long i : 8;
    };
    
    void f() {
      C x{1}, y{2};
      x.i <=> y.i; // OK
    }

    Overloaded functions and constraints

    DR 2918 deals with deduction from an overload set when multiple candidates succeed and have the same type; the most constrained function is selected. The following test case shows this new behavior:

    template<bool B> struct X {
      static void f(short) requires B; // #1
      static void f(short);            // #2
    };
    void test() {
      auto x = &X<true>::f;     // OK, deduces void(*)(short), selects #1
      auto y = &X<false>::f;    // OK, deduces void(*)(short), selects #2
    }

    Additional updates

    This section describes other enhancements in GCC 15.

    Fix for range-based for loops

    This paper fixes a long-standing problem with range-based for loops, which caused much grief in practice. The issue was that the lifetime of the temporaries used in the initializer of a range-based for loops weren’t extended, causing undefined behavior. In C++23, the problem was corrected and now the lifetime of the temporaries is extended to cover the whole loop.

    The effect is that the following program will print in C++20:

    ~T()
    loop

    But in C++23, it will print:

    loop
    ~T()

    It is possible to control this behavior with the -frange-for-ext-temps and -fno-range-for-ext-temps options.

    struct T {
      int arr[1];
      ~T() { std::printf ("~T()\n"); }
      const int *begin () const { return &arr[0]; }
      const int *end () const { return &arr[1]; }
    };
    const T& f(const T& t) { return t; }
    T g() { return T{42}; }
    
    int main ()
    {
      for (auto e : f (g ()))
        std::printf ("loop\n");
    }

    As an aside, this proposal added the fourth case where lifetime extension takes place. DR 2867, also implemented in GCC 15, added the fifth case, which deals with lifetime extension in structured bindings.

    Qualified lookup failures diagnosed early

    GCC 15 diagnoses certain invalid qualified lookups earlier, while parsing the template. When the scope of a qualified name is the current instantiation, and qualified lookup finds nothing at template definition time, then we know that qualified lookup will find nothing at instantiation time either (unless the current instantiation has dependent bases). 

    Therefore, such qualified name lookup failures can be diagnosed ahead of time. This is allowed by the C++ standard—[temp.res.general]/6 says: “The validity of a templated entity may be checked prior to any instantiation.” Consequently, the following program is rejected by GCC 15:  

    template<typename T>
    struct S {
      void foo() {
        int i = this->nothere;
      };
    };

    But this one isn’t:

    template<typename T>
    struct S : T {
      void foo() {
        int i = this->nothere;
      };
    };

    because nothere could be found in the dependent base.

    Concepts TS removed

    The support for Concepts TS was removed and -fconcepts-ts has no effect anymore. Programs using the Concepts TS features need to convert their code to C++20 Concepts. For example:

    template<typename T>
    concept C = true;
    
    C{T} void foo (T); // write template<C T> void foo (T);

    -fassume-sane-operators-new-delete

    GCC 15 gained the -fassume-sane-operators-new-delete option which can be used to adjust the behavior regarding optimizations around calls to replaceable global operators new and delete.  The manual provides more information about this option.

    C++ modules

    GCC 15 greatly improved the modules code. For instance, module std is now supported (even in C++20 mode).

    Compile-time speed improvements

    Code that uses a lot of template specializations should compile faster with GCC 15 due to the improvements in the hashing of template specializations.

    flag_enum

    GCC 15 has a new attribute called flag_enum (see the manual for more information). It can be used to suppress a -Wswitch warning emitted when enumerators are used in bitwise operations. For example, without the attribute the following test case:

    enum [[gnu::flag_enum]] E { cheer = 1, blue = 2 };
    void f (enum E e) {
      switch (e) {
      case blue|cheer:
      default:;
      }
    }

    would result in a case value ‘3’ not in enumerated type ‘E’ warning.

    New prvalue optimization

    GCC 15 tries harder to constant evaluate class prvalues used as function arguments. Previously, there was a discrepancy between the behavior of:

      Widget w{"Shepp"};
      do_something (w);

    and:

    do_something (Widget{“Shepp”});

    As a result of this optimization, GCC is able to compile code that it previously wasn’t.

    C++11 attributes in C++98

    GCC 15 allows C++11 attributes to be used even in C++98, which previous versions of GCC sometimes refused to compile. For example, the following code compiles even in C++98 mode:

    struct [[gnu::packed]] S { int sardines; };

    With -Wpedantic the compiler warns when it encounters a C++11 attribute in C++98 mode. 

    New and improved warnings

    GCC's set of warning options have been enhanced in GCC 15.

    -Wtemplate-body

    Errors in uninstantiated templates are IFNDR (ill-formed, no diagnostic required), but GCC progressively diagnoses more and more errors in template definitions ahead of time to catch errors sooner. For example:

    template<typename>
    void foo ()
    {
      const int n = 42;
      ++n; // error, no valid instantiation exists
    }

    For various reasons, the more aggressive behavior can be undesirable so GCC 15 added the -Wno-template-body option, which can be used to disable the template errors.

    -Wself-move

    The -Wself-move warning now warns even in a member-initializer-list:

    #include <utility>
    
    struct B {
    int i_;
    B(int) : i_(std::move(i_)) { } // warning
    };

    -Wdefaulted-function-deleted

    In GCC 15 we fixed our handling of [dcl.fct.def.default]. For example, the following test used to be rejected with an error:

    struct C {
      C(const C&&) = default;
    };

    But instead, the move constructor should be marked as deleted. The reason is that the function’s type doesn’t match the type of an implicit move constructor. GCC 15 accepts the code, but warns about it by default with this new warning.

    -Wheader-guard

    This new warning catches typos in header file guarding macros. For instance, including the following .h file:

    #ifndef STDIO_H
    #define STDOI_H
    extern void elmo ();
    extern void hope ();
    #endif

    will cause a warning to be emitted (if -Wall was specified): did you mean ‘STDIO_H’?

    -Wdangling-reference

    This warning has been improved in GCC 15. For instance, it no longer warns for empty classes.

    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. We would also like to thank Nathaniel Shead for his work on C++ modules.

    Last updated: April 25, 2025

    Related Posts

    • 6 usability improvements in GCC 15

    • How to implement C23 #embed in GCC 15

    • New C++ features in GCC 14

    • New C++ features in GCC 13

    • New C++ library features in GCC 14 15:58:29 [13/124]

    • Improvements to static analysis in the GCC 14 compiler

    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?

    The systemd cheat sheet presents the most common uses of systemctl, along with journalctl for displaying information about systemd activities from its logs.

    Get the cheat sheet
    Red Hat Developers logoLinkedInYouTubeTwitterFacebook

    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 Dasard
    • 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