【发布时间】:2021-02-21 22:31:09
【问题描述】:
以下程序:
#include <iostream>
struct A
{
A() { std::cout << "A()\n"; }
A(int) { std::cout << "A(int)\n"; }
};
struct C {
operator int() {
std::cout << "operator int\n";
return 42;
}
operator A() {
std::cout << "operator A\n";
return A();
}
};
int main() {
auto a = A{C{}};
};
编译并在运行时打印:
operator A
A()
这表明选择A 的移动构造函数来执行初始化,并调用C::operator A() 将C 转换为移动构造函数期望的类型。
或者,可以选择A::A(int) 构造函数,并调用C::operator int()。但是A::A(int) 显然失去了重载决议。
为什么会这样?我无法在标准中看到任何解释为什么 A 的移动构造函数赢得重载决议的规则。
(这个问题的灵感来自this answer;我不明白为什么该答案中的代码有效。)
【问题讨论】:
-
我认为因为通过
int需要 2 次转换,但标准只允许 1 次 -
@AlanBirtles 我不认为这就是原因。只允许一种用户定义的转换将参数类型转换为参数类型。在这两种情况下,都有一个用户定义的转换,即转换运算符。重载解决过程最终选择的对
A的构造函数的调用不算在内。这发生在重载决议完成之后。 -
由于复制省略,编译器似乎不会选择
A(int),但我不确定该规则来自哪里。 -
似乎强制复制省略导致移动构造函数成为首选。基于此gcc bug report 和this proposal,这似乎是意图,但标准对此并不十分清楚。该提案中的一些(但不是全部)措辞似乎已合并到工作草案中,因此当前的工作草案可能解决了歧义;我说不出来。
-
@cigien 我不明白该提案的相关性。似乎都是用移动替换副本,而不是完全省略移动。
标签: c++ c++17 overload-resolution