Porting to GCC 13

The GCC 13 release series differs from previous GCC releases in a number of ways. Some of these are a result of bug fixing, and some old behaviors have been intentionally changed to support new standards, or relaxed in standards-conforming ways to facilitate compilation or run-time performance.

Some of these changes are user visible and can cause grief when porting to GCC 13. This document is an effort to identify common issues and provide solutions. Let us know if you have suggestions for improvements!

C++ language issues

Header dependency changes

Some C++ Standard Library headers have been changed to no longer include other headers that were being used internally by the library. As such, C++ programs that used standard library components without including the right headers will no longer compile.

The following headers are used less widely in libstdc++ and may need to be included explicitly when compiling with GCC 13:

Implicit move rules change

GCC 13 implements C++23 P2266 which simplified the rules for implicit move. As a consequence, valid C++20 code that relies on a returned id-expression's being an lvalue may change behavior or fail to compile in C++23. For example:


   decltype(auto) f(int&& x) { return (x); }  // returns int&&; previously returned int&
   int& g(int&& x) { return x; }  // ill-formed; previously well-formed

Two-stage overload resolution for implicit move removed

GCC 13 removed the two-stage overload resolution when performing implicit move, whereby the compiler does two separate overload resolutions: one treating the operand as an rvalue, and then (if that resolution fails) another one treating the operand as an lvalue. In the standard this was introduced in C++11 and implemented in gcc in r251035. In r11-2412, the fallback overload resolution was disabled in C++20 (but not in C++17). Then C++23 P2266 removed the fallback overload resolution, and changed the implicit move rules once again.

The two overload resolutions approach was complicated and quirky, so users should transition to the newer model. This change means that code that previously didn't compile in C++17 will now compile, for example:


   struct S1 { S1(S1 &&); };
   struct S2 : S1 {};

   S1
   f (S2 s)
   {
     return s; // OK, derived-to-base, use S1::S1(S1&&)
   }

Conversely, code that used to work in C++17 may not compile anymore. For example, the following example used to compile in C++11...17 because we performed two separate overload resolutions: one treating the operand as an rvalue, and then (if that resolution failed) another one treating the operand as an lvalue.


   struct W {
     W();
   };

   struct F {
     F(W&);
     F(W&&) = delete;
   };

   F fn ()
   {
     W w;
     return w; // use w as rvalue -> use of deleted function F::F(W&&)
   }