重载数组引用参数的编译器方差
正如预期的那样,以下程序被 GCC、Clang 和 MSVC 接受(对于各种编译器和语言版本):
// #g.1: rvalue reference function parameter
constexpr bool g(int&&) { return true; }
// #g.2: const lvalue reference function parameter
constexpr bool g(int const&) { return false; }
static_assert(g(0), ""); // OK: picks #g.1
类似的情况,但对于数组 {rvalue, const lvalue} 引用参数的重载是然而,被 GCC 拒绝,而被 Clang 和 MSVC 接受:
#include <cstddef>
// #f.1: rvalue ref overload
template<std::size_t size>
constexpr bool f(int (&&)[size]) { return true; }
// #f.2: lvalue ref overload
template<std::size_t size>
constexpr bool f(int const (&)[size]) { return false; }
static_assert(f({1, 2, 3}), ""); // Clang: OK (picks #f.1)
// MSVC: OK (picks #f.1)
// GCC: Error (picks #f.2)
DEMO。
乍一看这看起来像是一个 GCC 错误,但我想确定一下。
问题
- 这里有哪个编译器?
The following program is, as expected, accepted by both GCC, Clang and MSVC (for various compiler and language versions):
// #g.1: rvalue reference function parameter
constexpr bool g(int&&) { return true; }
// #g.2: const lvalue reference function parameter
constexpr bool g(int const&) { return false; }
static_assert(g(0), ""); // OK: picks #g.1
The similar case but for overloads over an array {rvalue, const lvalue} reference parameter is, however, rejected by GCC, whilst it is accepted by Clang and MSVC:
#include <cstddef>
// #f.1: rvalue ref overload
template<std::size_t size>
constexpr bool f(int (&&)[size]) { return true; }
// #f.2: lvalue ref overload
template<std::size_t size>
constexpr bool f(int const (&)[size]) { return false; }
static_assert(f({1, 2, 3}), ""); // Clang: OK (picks #f.1)
// MSVC: OK (picks #f.1)
// GCC: Error (picks #f.2)
DEMO.
At first glance this looks like a GCC bug, but I'd like to be sure.
Question
- Which compiler(s) is(/are) right here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
GCC 错误
由于参数是初始化列表,因此适用特殊规则,按照 [over.ics.list]/1:
由于两个重载的参数类型都是引用类型,[over. ics.list]/9 适用 [强调我的]:
尽管不规范,但这意味着调用
对于这两个候选函数是(根据 [over.ics .list]/6)就像
[over.ics.ref],特别是 [over.ics.ref]/3,简单地介绍了这种纯右值参数可以直接绑定到右值引用或 const 左值引用,这意味着两个候选者都是可行的候选者。
我们转向 [over.ics.ran]/3.2 .3:
因此,GCC 错误地选择了 const 左值引用重载
#f.2
,如#f.1
是最好的可行函数。我们可能会注意到,如果我们显式传递右值数组而不是使用初始值设定项列表,则所有编译器都会同意
#f.1
重载 (DEMO):GCC 在原始示例中的无效行为可以说是由于 GCC 如何对待对应函数参数类型为数组引用类型。
错误报告
GCC is wrong
Since the argument is an initializer list, special rules apply, as per [over.ics.list]/1:
As the parameter type for both overloads are references types, [over.ics.list]/9 applies [emphasis mine]:
Albeit non-normative, this means that the call
for both of the candidate functions is (as per [over.ics.list]/6) as if
[over.ics.ref], particularly [over.ics.ref]/3, simply covers that this kind of prvalue argument can bind directly both to an rvalue reference or to a const lvalue reference, meaning both candidates are viable candidates.
We turn to [over.ics.ran]/3.2.3:
Thus, GCC is wrong to pick the const lvalue reference overload
#f.2
, as#f.1
is the best viable function.We may note that all compilers agree on the
#f.1
overload if we explicitly pass an rvalue array instead of using an initializer list (DEMO):GCC's invalid behavior in the original example is thus arguably specifically due to how GCC treats initializer list arguments for candidate functions whose corresponding function parameter type is of array reference type.
Bug report