【问题标题】:Overload resolution with rvalue and lvalue reference candidates when passed empty initializer list传递空初始值设定项列表时使用右值和左值引用候选者重载分辨率
【发布时间】:2020-10-17 08:33:42
【问题描述】:
struct Foo {};
struct Bar {};

int Baz(const Foo&) { return 0; }
int Baz(Bar&&) { return 1; }

int main()
{
  return Baz({});
}

这个调用有歧义吗? MSVC 选择右值引用重载。 GCC 说这是模棱两可的。 Clang 也选择右值引用,但如果它是 int Baz(std::vector<Bar>&&) 或 Bar 获得一个采用 std::initializer_list 的构造函数,则不再选择。

{}Bar 是标准转换顺序吗?

有人可以解释为什么这适用或不适用:

  1. 标准转换序列 S1 优于标准 转换序列S2 if

[...]

c) 或者,如果不是这样,S1 和 S2 都绑定到引用参数,而不是引用限定成员函数的隐式对象参数,并且 S1 将右值引用绑定到右值,而 S2 绑定对右值的左值引用

(https://en.cppreference.com/w/cpp/language/overload_resolution)

【问题讨论】:

  • A standard conversion sequence 是的,但那是您实际上拥有一种类型并将其转换为另一种类型的时候。但是{}.. 是什么类型?请参阅viable functions 部分。
  • 措辞有点缺陷(wg21.link/cwg1536),但我们都知道创建了一个临时对象,并且绑定右值引用比绑定左值引用更好。

标签: c++ language-lawyer


【解决方案1】:

正如问题中已经提到的那样,标准在 [over.ics.rank]/(3.2) 中说:

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

...

  • S1S2 是引用绑定 (11.6.3),它们都没有引用未声明 ref-qualifier 的非静态成员函数的隐式对象参数,S1 绑定一个右值引用到右值,S2 绑定左值引用

...

所以这里 GCC 是错误的。

其实 Clang 也可以通过在结构体中添加默认构造函数来进行类似的欺骗:

struct Foo {
    constexpr Foo() = default;
};
struct Bar {
    constexpr Bar() = default;
};

constexpr int Baz(const Foo&) { return 0; }
constexpr int Baz(Bar&&) { return 1; }

int main() {
  static_assert( Baz({}) == 1 );
}

MSVC是这里正确选择Baz(Bar&&)重载的编译器,演示:https://gcc.godbolt.org/z/sMcx9ro1x

【讨论】:

    猜你喜欢
    • 2017-03-26
    • 2013-08-20
    • 1970-01-01
    • 2015-03-08
    • 2016-01-06
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    相关资源
    最近更新 更多