GCC is wrong here.
All references are to N4431, the latest C++ WD.
[tl;dr: There’s a difference between a function being inline (or more precisely, being an inline function, as defined in 7.1.2/2) and being declared with the inline
specifier. The constexpr
specifier makes a function inline, but isn’t an inline
specifier.]
Specifiers are described in subclause 7.1 of the C++ standard, and are an element of the grammar. Therefore, whenever standard talks about a foo
specifier appearing somewhere, it means that specifier literally appeared within the (parse tree of the) source code. The inline
specifier is a function-specifier, described in subclause 7.1.2, and its effect is to make a function be an inline function. (7.1.2)/2:
A function declaration (8.3.5, 9.3, 11.3) with an
inline
specifier declares an inline function.
There are two other ways to declare an inline function, without using an inline
specifier. One is described in (7.1.2)/3:
A function defined within a class definition is an inline function.
The other is described in (7.1.5)/1:
constexpr functions and constexpr constructors are implicitly
inline (7.1.2).
Neither of these says that the behavior is as if an inline
specifier were present, merely that the function is an inline function.
So why does this rule exist?
There’s a simpler form of this rule in (7.1.2)/3:
If the
inline
specifier is used in a friend declaration, that declaration shall be a definition or the function shall have previously been declared inline.
The purpose of this is to allow friend declarations to be ignored in most cases — they are not permitted to add “new information” to the befriended entity, except in the special case where they are defining a friend function. (This in turn allows an implementation to delay parsing a class definition until it’s “needed”.) Thus we also see, in (8.3.6)/4:
If a friend declaration specifies a default argument expression, that declaration shall be a definition and shall be the only declaration of the function or function template in the translation unit.
And the same applies to a declaration of a friend specialization of a function template: if it could add extra information, then implementations could not delay parsing the class definition.
Now, note that this rationale does not apply to constexpr
: if the constexpr
specifier appears on any declaration of a function, it must appear on every declaration, per (7.1.5)/1. Since there is no “new information” here, there is no need for a restriction.