重载分辨率:首选直接转换运算符(由于复制删除)?

pre*_*ous 10 c++ language-lawyer overload-resolution copy-elision c++17

给定

struct E
{
};

struct P
{
    explicit P(E) {}
};

struct L
{
    operator E() {return {};}
    operator P() {return P{E{}};}
};
Run Code Online (Sandbox Code Playgroud)

根据C ++ 17语言标准,该表达式应该P{L{}}编译吗?

不同的编译器产生不同的结果:

  • gcc(trunk):好的
  • gcc 8.3:错误(超载不明确)
  • gcc 7.4:好的
  • lang声(trunk):好的
  • 铛8.0.0:好
  • 铛7.0.0:好的
  • msvc v19.20:错误(不明确的过载)
  • icc 19.0.1:错误(多个构造函数实例匹配)

Bar*_*rry 4

我认为根据标准正确的行为是模棱两可。

[dcl.init]/17.1

如果初始化器是(非括号)braced-init-list 或 is = braced-init-list,则对象或引用是列表初始化的。

[dcl.init.list]/3.6

否则,如果 T 是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决策([over.match]、[over.match.list])选择最好的构造函数。如果需要缩小转换(见下文)来转换任何参数,则该程序格式错误。

[over.match.list]只是讨论选择构造函数。我们有两个可行的选择:P(E)通过L{}.operator E()P(P&&)(隐式移动构造函数)通过L{}.operator P()​​ 。没有哪个更好。


然而,这很容易让人想起CWG 2327

struct Cat {};
struct Dog { operator Cat(); };

Dog d;
Cat c(d);
Run Code Online (Sandbox Code Playgroud)

正如问题所表明的那样,当前调用Cat(Cat&&)而不只是调用d.operator Cat(),并建议我们实际上也应该考虑转换函数。但这仍然是一个悬而未决的问题。我不确定 gcc 或 clang 针对此问题(或针对首先提出的类似示例)做了什么,但根据您的结果,我怀疑他们认为直接转换函数是L{}.operator P()更好的匹配,并且只是去做。