重载数组引用参数的编译器方差

发布于 2025-01-15 05:19:17 字数 1059 浏览 3 评论 0原文

正如预期的那样,以下程序被 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

雪若未夕 2025-01-22 05:19:17

GCC 错误

由于参数是初始化列表,因此适用特殊规则,按照 [over.ics.list]/1

当参数是初始化列表 ([dcl.init.list]) 时,它不是表达式,并且应用特殊规则将其转换为参数类型。

由于两个重载的参数类型都是引用类型,[over. ics.list]/9 适用 [强调我的]:

否则,如果参数是引用,请参阅 [over.ics.ref]。

[注 2:本子条款中的规则将适用于初始化参考的底层临时文件。 — 尾注]

尽管不规范,但这意味着调用

<前><代码>f({1, 2, 3})

对于这两个候选函数是(根据 [over.ics .list]/6)就像

template<typename T>
using type = T;

f(type<int[3]>{1, 2, 3});  // prvalue array

[over.ics.ref],特别是 [over.ics.ref]/3,简单地介绍了这种纯右值参数可以直接绑定到右值引用或 const 左值引用,这意味着两个候选者都是可行的候选者。

我们转向 [over.ics.ran]/3.2 .3

/3.2 标准转换序列S1是更好的转换序列
比标准转换序列 S2
if

  • [...]
  • /3.2.3 S1 和 S2 包含引用绑定 ([dcl.init.ref]),并且都不引用非静态成员的隐式对象参数
    声明的函数没有引用限定符,并且 S1 绑定了一个右值
    对右值的引用,S2 绑定左值引用

因此,GCC 错误地选择了 const 左值引用重载 #f.2,如 #f.1是最好的可行函数。


我们可能会注意到,如果我们显式传递右值数组而不是使用初始值设定项列表,则所有编译器都会同意 #f.1 重载 (DEMO):

template<typename T>
using type = T;

// All compilers agree: OK
static_assert(f(type<int[3]>{1, 2, 3}));

GCC 在原始示例中的无效行为可以说是由于 GCC 如何对待对应函数参数类型为数组引用类型。


错误报告

  • 错误 104996 - 通过右值/const 左值数组引用重载解析init 的参数。 list 参数错误地选择了 const 左值引用。超载

GCC is wrong

Since the argument is an initializer list, special rules apply, as per [over.ics.list]/1:

When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.

As the parameter type for both overloads are references types, [over.ics.list]/9 applies [emphasis mine]:

Otherwise, if the parameter is a reference, see [over.ics.ref].

[Note 2: The rules in this subclause will apply for initializing the underlying temporary for the reference. — end note]

Albeit non-normative, this means that the call

f({1, 2, 3})

for both of the candidate functions is (as per [over.ics.list]/6) as if

template<typename T>
using type = T;

f(type<int[3]>{1, 2, 3});  // prvalue array

[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:

/3.2 Standard conversion sequence S1 is a better conversion sequence
than standard conversion sequence S2
if

  • [...]
  • /3.2.3 S1 and S2 include reference bindings ([dcl.init.ref]) and neither refers to an implicit object parameter of a non-static member
    function declared without a ref-qualifier, and S1 binds an rvalue
    reference to an rvalue and S2 binds an lvalue reference

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):

template<typename T>
using type = T;

// All compilers agree: OK
static_assert(f(type<int[3]>{1, 2, 3}));

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

  • Bug 104996 - Overload resolution over rvalue/const lvalue array reference parameters for an init. list argument incorrectly picks the const lvalue ref. overload
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文