朋友模板专业化声明中不允许使用 Constexpr?

Constexpr is not allowed in declaration of friend template specialization?

我正在将 C++14-constexpr 代码库从 Clang 移植到最新的 g++-5.1。考虑以下自制 bitset class 的精简代码片段,该代码片段自 Clang 3.3 的繁荣时期(到现在将近 2 年!)

以来一直在正确编译
#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 在 Wandbox 上。但是,g++-5.1 和当前的 trunk 版本报错:

'constexpr' is not allowed in declaration of friend template specialization

问题:这是已知的 g++ 错误还是 Clang 不符合最新标准?

注意:上面只使用了C++11风格的constexpr特性,因为operator==内部没有发生修改,所以看起来模板、朋友和 constexpr 之间的一些奇怪的干扰。

更新:在 Bugzilla 上归档为 bug 65977

这里GCC错了

所有参考资料均指向最新的 C++ WD N4431。

[tl;dr:inline 函数(或者更准确地说,inline 函数 之间的区别在 7.1.2/2 中)并使用 inline 说明符声明。 constexpr 说明符使函数内联,但不是 inline 说明符。]

说明符在 C++ 标准的子条款 7.1 中进行了描述,并且是语法的一个元素。因此,每当标准谈到 foo 说明符出现在某处时,它意味着说明符字面上出现在源代码的(分析树)中。 inline 说明符是一个 函数说明符 ,如 7.1.2 节所述,其作用是使函数成为内联函数。 (7.1.2)/2:

A function declaration (8.3.5, 9.3, 11.3) with an inline specifier declares an inline function.

还有另外两种方法可以不使用 inline 说明符来声明内联函数。一个在 (7.1.2)/3:

中描述

A function defined within a class definition is an inline function.

另一个在(7.1.5)/1中有描述:

constexpr functions and constexpr constructors are implicitly inline (7.1.2).

这些都没有说行为就像 inline 说明符 一样,只是说该函数是一个内联函数。

那么为什么会有这条规则呢?

(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.

这样做的目的是允许在大多数情况下忽略友元声明——它们不允许将 "new information" 添加到被友化的实体中,除非它们正在定义友元函数的特殊情况. (这又允许实现延迟解析 class 定义,直到它是 "needed"。)因此我们也看到,在 (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.

这同样适用于函数模板的友元特化的声明:如果它可以添加额外的信息,那么实现就不会延迟解析 class 定义。

现在,请注意这个基本原理 不适用于 constexpr:如果 constexpr 说明符出现在函数的任何声明中,它必须出现在 每个 声明中,根据 (7.1.5)/1。由于这里没有"new information",所以不需要限制