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

时间:2019-04-30 18:45:09

标签: c++ language-lawyer c++17 overload-resolution copy-elision

给予

struct E
{
};

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

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

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

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

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

1 个答案:

答案 0 :(得分:4)

我认为每个标准的正确行为都是模棱两可的。

[dcl.init]/17.1

  

如果初始化程序是一个(非括号括起来的)braced-init-list或= 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);

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