New C++ features in GCC 14 | Red Hat Developer (2024)

The next major version of theGNU 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 onCompiler Explorer and similar pages.

Like the article I wrote aboutGCC 13, this article describes only new features implemented in the C++ front end; it does not discuss developments in the C++ language itself which will have a separate blog post.Interesting changes in the standard C++ library that comes with GCC 14 will be described in a separate blog post as well.

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 arestill experimental in GCC 14.

C++26 features

Trivial infinite loops and UB

Thisproposal brings C and C++ a little closer together.The C++ standard, unlike the C standard, definedforward 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 atrivially 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 offorward progress.For example, in GCC 14, the following snippet no longer causes undefined behavior (in all C++ dialects, not only C++26):

voidg (){ 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 forstring-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, implementsP2361R6 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++26static_assert (true, "\x{20}"); // warning in C++26 in pedantic mode

Constexpr cast from void*

GCC 14 implementsP2738R1, 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

Withthis 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 proposalP2169R4.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 ();voidg (){ 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

Deducingthis

GCC 14 implementedthis 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 anexplicit 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 withref-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 Sstd::as_const(s).get_add(3); // Self is deduced as const S&

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

intmain (){ 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 moreexamples 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 witherror: ‘arr’ is not a constant expression. The rules have beenadjusted and the code works as expected with GCC 14 (in all C++ dialects).

consteval needs to propagate up

Implementingthis proposal in GCC 14 turned out to be somewhat knotty.It causes certain functions to be promoted to consteval, that is, making themimmediate 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 animmediate 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 calledimmediate-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 onlyimmediate-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-escalationcommand-line option.

CTAD from inherited constructors

GCC 14 supportsP2582R1, 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 ourC++ 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

SinceCWG 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 thePR109159 fix, but it caused issues in practice.The problem was mitigated byCWG 2735 which GCC 14 and GCC 13 implement. Consequently, the test fromCWG 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 atemplate-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 visitthe 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 botha andb.

-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};voidg (){ 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.

New C++ features in GCC 14 | Red Hat Developer (2024)

FAQs

Which GCC supports C++ 14? ›

GCC finished support for C++14 in GCC 5, and made C++14 the default C++ standard in GCC 6.

What is the latest version of GCC C++? ›

The latest major version of the GNU Compiler Collection (GCC), 13.1, was released in April 2023. Like every major GCC release, this version brings many additions, improvements, bug fixes, and new features.

Which GCC version supports C++20? ›

C++20 Support in GCC

C++20 features are available since GCC 8. To enable C++20 support, add the command-line parameter -std=c++20 (use -std=c++2a in GCC 9 and earlier) to your g++ command line. Or, to enable GNU extensions in addition to C++20 features, add -std=gnu++20 .

Which GCC has C ++ 17? ›

Compiler support

GCC has had complete support for C++17 language features since version 8. Clang 5 and later supports all C++17 language features. Visual Studio 2017 15.8 (MSVC 19.15) and later supports all C++17 language features.

Should I use GCC or G ++ for C++? ›

GCC and G++ are both used for compiling C and C++ languages. However, GCC is used for compiling C programs while G++ is used for compiling C++ programs. G++ automatically links files in the libraries of std C++ during object file linking, which does not happen with GCC.

Can I run C++ with GCC? ›

GCC recognizes files with these names and compiles them as C++ programs even if you call the compiler the same way as for compiling C programs (usually with the name gcc ). However, the use of gcc does not add the C++ library. g++ is a program that calls GCC and automatically specifies linking against the C++ library.

What is the most updated C++? ›

C++20 is a version of the ISO/IEC 14882 standard for the C++ programming language. C++20 replaced the prior version of the C++ standard, called C++17, and was later replaced by C++23.

What C++ standard does GCC support? ›

GCC supports the original ISO C++ standard published in 1998, and the 2011, 2014, 2017 and mostly 2020 revisions. The original ISO C++ standard was published as the ISO standard (ISO/IEC 14882:1998) and amended by a Technical Corrigenda published in 2003 (ISO/IEC 14882:2003).

How to check version of C++ in GCC? ›

You can find out about this in the shell with: gcc -v or clang -v each version of a compiler has a default standard of C++ it compiles. This default you will find out with a quick google search involving your compiler's name and its version. Also, you can change the C++ standard using a flag.

Is Clang better than GCC? ›

Clang is much faster and uses far less memory than GCC. Clang aims to provide extremely clear and concise diagnostics (error and warning messages), and includes support for expressive diagnostics. GCC's warnings are sometimes acceptable, but are often confusing and it does not support expressive diagnostics.

Which GCC version supports C++23? ›

Compiler support

GCC added partial, experimental C++23 support in 2021 in version 11 through the option -std=c++2b or -std=c++23 It also has an option to enable GNU extensions in addition to the experimental C++23 support, -std=gnu++2b .

What's new in C++23? ›

C++23 continues to enhance lambda expressions by introducing new features such as simplified syntax for not capturing any variables and improved constexpr capabilities. These improvements make lambdas more powerful and easier to use in a wider range of contexts, including compile-time programming.

Is C ++ 14 and C++17 same? ›

C++14 was made better by C++17 that came out in 2017 and included several more features meant to improve its capabilities and solve some problems that programmers regularly encounter.

What is the current version of C++? ›

Standardization
YearISO/IEC StandardInformal name
201414882:2014C++14, C++1y
201714882:2017C++17, C++1z
202014882:2020C++20, C++2a
2023C++23
3 more rows

What is the current version of GCC? ›

The latest major version of the GNU Compiler Collection (GCC), 13.1, was released in April 2023. Like every major GCC release, this version will bring many additions, improvements, bug fixes, and new features.

What version of C++ does GCC 4.8 support? ›

GCC provides experimental support for the 2011 ISO C++ standard. This support can be enabled with the -std=c++11 or -std=gnu++11 compiler options; the former disables GNU extensions. As of GCC 4.8. 1, GCC's C++11 mode implements all of the major features of the C++11 standard produced by the ISO C++ committee.

Which GCC version supports C ++ 11? ›

C++11 is fully supported by Clang 3.3 and later. C++11 is fully supported by GCC 4.8. 1 and later.

What is the default C++ for 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.

Top Articles
Latest Posts
Recommended Articles
Article information

Author: Duane Harber

Last Updated:

Views: 5567

Rating: 4 / 5 (51 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Duane Harber

Birthday: 1999-10-17

Address: Apt. 404 9899 Magnolia Roads, Port Royceville, ID 78186

Phone: +186911129794335

Job: Human Hospitality Planner

Hobby: Listening to music, Orienteering, Knapping, Dance, Mountain biking, Fishing, Pottery

Introduction: My name is Duane Harber, I am a modern, clever, handsome, fair, agreeable, inexpensive, beautiful person who loves writing and wants to share my knowledge and understanding with you.