I'm porting a C++14-constexpr
codebase from Clang to the latest g++-5.1. Consider the following reduced code snippet of a home-grown bitset
class that has been compiling correctly since the halcyon days of Clang 3.3 (almost 2 years now!)
#include <cstddef>
template<std::size_t>
class bitset;
template<std::size_t N>
constexpr bool operator==(const bitset<N>& lhs, const bitset<N>& rhs) noexcept;
template<std::size_t N>
class bitset
{
friend constexpr bool operator== <>(const bitset<N>&, const bitset<N>&) noexcept;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <-- error from this piece
};
template<std::size_t N>
constexpr bool operator==(const bitset<N>& /* lhs */, const bitset<N>& /* rhs */) noexcept
{
return true;
}
int main() {}
Live example on Wandbox. However, g++-5.1 and the current trunk release give an error:
'constexpr' is not allowed in declaration of friend template specialization
Question: is this a known g++ bug or is Clang not conforming to the latest Standard?
Note: the above only uses C++11 style constexpr
features, since there are no modifications taking place inside operator==
, so it seems some weird interference between templates, friends and constexpr.
UPDATE: filed as bug 65977 on Bugzilla.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With