重载分辨率:是否首选直接转换运算符(由于复制省略)?

Overload-Resolution: Is a direct conversion operator preferred (as a consequence of copy-elision)?

本文关键字:于复制 复制 运算符 是否 分辨率 转换 重载      更新时间:2023-10-16

给定

struct E
{
};
struct P
{
explicit P(E) {}
};
struct L
{
operator E() {return {};}
operator P() {return P{E{}};}
};

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

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

  • 海湾合作委员会(后备箱):确定
  • GCC 8.3:错误(过载不明确)
  • GCC 7.4:确定
  • 叮当(树干):好的
  • 叮当 8.0.0:确定
  • 叮当 7.0.0:确定
  • MSVC v19.20:错误(过载不明确)
  • ICC 19.0.1:错误(多个构造函数实例匹配)

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

[dcl.init]/17.1:

如果初始值设定项是(非括号)大括号初始化列表或 is = 大括号初始化列表,则对象或引用是列表初始化的。

[dcl.init.list]/3.6:

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

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


但是,这很容易让人想起CWG 2327:

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

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