Porting to GCC 7

The GCC 7 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 in order to support new standards, or relaxed in standards-conforming ways to facilitate compilation or run-time performance. Some of these changes are not visible to the naked eye and will not cause problems when updating from older versions.

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

Preprocessor issues

C language issues

C++ language issues

Stricter rules when using templates

GCC 7 no longer accepts various ill-formed constructs involving the use of templates. The C++ standard says:

14.6/8: "If a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, the program is ill-formed; no diagnostic is required. If the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the template, the program is ill-formed; no diagnostic is required."

As a consequence, the following examples are invalid and G++ will either no longer compile them, or will warn. G++ used to treat this->member, where member has a non-dependent type, as type-dependent and no longer does.


struct C;
struct A {
  C fn1();
};
template <typename> class B : A {
  void fn2() { fn1().x; }
};
results in
warning: invalid use of incomplete type 'struct C'

struct A {
  int x;
};
template <typename> struct B : A {
  void fn1() { foo (this->x); }
};
results in
error: there are no arguments to 'foo' that depend on a template parameter, so a declaration of 'foo' must be available

struct A {
  void* a;
};
template <typename> class B : A {
  void fn1() { this->a[0]; }
};
results in
error: 'void*' is not a pointer-to-object type
because there is no instantiation of that template that can be valid; it will always dereference a void*.

Mangling change for conversion operators

GCC 7 changes the name mangling for a conversion operator that returns a type using the abi_tag attribute, see PR 71712. This affects code that has conversions to std::string, for example:

struct A {
  operator std::string() const;
};

Code using such conversions might fail to link if some objects are compiled with GCC 7 and some are compiled with older releases.

Null pointer constants

When compiling as C++11 or later, GCC 7 follows the revised definition of a null pointer constant. This means conversions to pointers from other types of constant (such as character literals and boolean literals) are now rejected.

void* f() {
  char* p = '\0';
  return false;
}
error: invalid conversion from 'char' to 'char*' [-fpermissive]
   char* p = '\0';
             ^~~~
error: cannot convert 'bool' to 'void*' in return
   return false;
          ^~~~~

Such code should be fixed to use a valid null pointer constant such as 0 or nullptr. Care should be taken when fixing invalid uses of '\0' as a pointer, as it may not be clear whether the intention was to create a null pointer (p = 0;), to create an empty string (p = "";), or to write a null terminator to the string (*p = '\0';).

Header dependency changes

Several C++ Standard Library headers have been changed to no longer include the <functional> header. As such, C++ programs that used components defined in <functional> without explicitly including that header will no longer compile.

Previously components such as std::bind and std::function were implicitly defined after including unrelated headers such as <memory>, <future>, <mutex>, and <regex>. Correct code should #include <functional> to define them.

Additional overloads of abs

As required by the latest C++ draft, all overloads of the abs function are declared by including either of <cstdlib> or <cmath> (and correspondingly by either of <stdlib.h> or <math.h>). Previously <cmath> only declared the overloads for floating-point types, and <cstdlib> only declared the overloads for integral types.

As a result of this change, code which overloads abs may no longer compile if the custom overloads conflict with one of the additional overloads in the standard headers. For example, this will not compile:

#include <stdlib.h>
float abs(float x) { return x < 0 ? -x : x; }

The solution is to use the standard functions, not define conflicting overloads. For portability to previous versions of GCC and other implementations the abs(float) function can be brought into scope by including <cmath and adding a using-declaration:

#include <stdlib.h>
#include <cmath>    // ensure std::abs(float) is declared
using std::abs;

Additionally, calling abs with an argument of unsigned type is now ill-formed after inclusion of any standard abs overload.

std::ios_base::failure

When iostream objects are requested to throw exceptions on stream buffer errors, the type of exception thrown has changed to use the new libstdc++ ABI introduced in GCC 5. Code which does catch (const std::ios::failure&) or similar will not catch the exception if it is built using the old ABI. To ensure the exception is caught either compile the catch handler using the new ABI, or use a handler of type std::exception (which will catch the old and new versions of std::ios_base::failure) or a handler of type std::system_error.

Changes to std::function constructed with std::reference_wrapper

Prior to GCC 7 a std::function constructed with a std::reference_wrapper<T> would unwrap the argument and store a target of type T, and target_type() would return typeid(T). GCC 7 has been changed to match the behavior of other implementations and not unwrap the argument. This means the target will be a std::reference_wrapper<T> and target_type() will return typeid(std::reference_wrapper<T>). Code which depends on the target type may need to be adjusted appropriately.

Changes for array support in std::shared_ptr

The behavior of std::shared_ptr<T[]> and std::shared_ptr<T[N]> has changed to match the semantics in the C++17 draft. Previously specializations of std::shared_ptr for array types had unhelpful semantics and were hard to use correctly, so the semantics have changed to match the C++17 behavior in GCC 7. Code which uses specializations for array types may continue to work in C++11 and C++14 modes, but not in C++17 mode. It is possible, although unlikely, that some valid uses of array specializations will no longer work with GCC 7 even in C++11 or C++14 modes. All uses of array specialization should be adjusted to match the C++17 semantics which will be standardized soon. Code which only uses specializations of std::shared_ptr<T> for non array-type is unaffected.

Marek Polacek Fedora mass rebuild 2017 on x86_64