在友元模板特化声明中不允许使用constexpr?

32

我正在将一个使用C++14-constexpr的代码库从Clang迁移到最新的g++-5.1。考虑到下面这个简化的代码片段,它是一个自制的bitset类,自从Clang 3.3时代以来一直能够正确编译(已经接近两年了!)

#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() {}

Wandbox上的实时示例。然而,g++-5.1和当前的trunk版本会报错:

'constexpr'在友元模板特化程序的声明中不允许

问题:这是已知的g++ bug还是Clang没有遵守最新标准?

注意:以上仅使用C++11风格的constexpr特性,因为在operator==内部没有进行任何修改,所以似乎是模板、友元和constexpr之间的一些奇怪干扰。

更新:已经在Bugzilla上作为bug 65977报告了。


4.8.2版本报错,指出存在多余的“inline”说明符... - Columbo
我没有看到任何明显的答案,可能有必要打开一个gcc错误报告,特别是因为clang和gcc之间存在实现差异。我没有看到与此相关的任何缺陷,尽管Marco的答案可能是正确的,但对我来说并不明显。 - Shafik Yaghmour
我个人会把赏金留在原地,这样答案就可以获得更多的赞。这是一个完美的答案,值得得到超过2个赞。 - Shafik Yaghmour
那可能是我花费的最好的50个声望点 :-) - Shafik Yaghmour
我了解你是第一个获得“constexpr”标签铜牌的人,所以我明白为什么你认为这是值得花费声望的好事情(就像我为我的声望感到自豪一样)。 - TemplateRex
我花了足够的时间在语言的边缘,以至于这些类型的精彩回答非常宝贵。 - Shafik Yaghmour
1个回答

34

GCC在这里是错的。

所有引用都是针对最新的C++ WD(N4431)。

[简述:一个函数被定义为内联函数(如7.1.2/2中定义),与使用inline指示符声明是有区别的。使用constexpr指示符使函数成为内联函数,但不是inline指示符。]

C++标准中的子句7.1描述了指示符,并且是语法的一个元素。因此,每当标准谈到某个foo指示符出现在某个地方时,它意味着该指示符实际上出现在(源代码的解析树中)。inline指示符是一个函数指示符,在子句7.1.2中进行了描述,其作用是使函数成为内联函数。(7.1.2)/2:

带有inline指示符的函数声明(8.3.5、9.3、11.3) 声明了一个内联函数

还有两种方式可以声明内联函数,而不使用inline指示符。其中一种在(7.1.2)/3中进行了描述:

在类定义中定义的函数是内联函数。

另一种被描述在(7.1.5)/1中:

constexpr函数和constexpr构造函数默认为内联函数(7.1.2)。

这两种方式都没有说行为就像存在一个inline指示符一样,只是该函数是内联函数。

那么这条规则为什么存在呢?

在(7.1.2)/3中有一个更简单的形式:

如果在友元声明中使用了inline说明符,则该声明应为定义或该函数先前应已被声明为内联。其目的是允许在大多数情况下忽略友元声明 - 它们不允许向被授权实体添加“新信息”,除非它们定义了友元函数。这反过来允许实现将类定义的解析延迟到“需要”时。因此我们还可以在(8.3.6)/4中看到如下规定:如果友元声明指定了默认参数表达式,则该声明必须为定义并且必须是翻译单元中该函数或函数模板的唯一声明。同样适用于函数模板的友元特化的声明:如果它可以添加额外信息,则实现将无法延迟解析类定义。现在,请注意,这种理由不适用于constexpr:如果任何函数的声明上出现了constexpr说明符,则必须在每个声明上都出现,参见(7.1.5)/1。由于没有“新信息”,因此不需要限制。

1
非常好的答案,谢谢!我会提交一个gcc错误报告(实际上是3个,因为涉及到constexpr元组功能的库DR 2275和2301也尚未在libstdc++中实现)。 - TemplateRex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接