重载决议:是否优先选择直接转换运算符(由于复制省略)?

12
< p > 给定
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:可以编译;clang(trunk):可以编译;clang 8.0.0:可以编译;clang 7.0.0:可以编译;msvc v19.20:错误(重载不明确);icc 19.0.1:错误(存在多个构造函数实例匹配)。

3
虽然这本身是一个有趣的问题,但我更想知道您为什么会问这个问题?导致您提出这个问题的真正问题是什么? - Some programmer dude
@Someprogrammerdude 当我从gcc 7.4升级到8.3时,我的一些代码停止编译。我知道如何解决,但我想了解代码是否符合标准。所有提到的gcc和clang版本都无法在C++14模式下编译代码。 - precarious
1个回答

4
我认为标准的正确行为应该是模棱两可的。

[dcl.init]/17.1:

如果初始化器是一个非圆括号的花括号初始化列表或者是等于号后面的花括号初始化列表,那么对象或引用就会进行列表初始化。

[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()更匹配,因此就采用了这种方式。


我很高兴我的直觉没有错。最近主要编译器的所有版本似乎都跳过了CWG 2327中示例的移动构造函数。我不得不回到Visual Studio 2015(msvc v19.0)才能看到这个问题。 - precarious

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接