[英]Call of overloaded static_cast is ambiguous
我有一些像这样的代码
struct B
{
B() {}
B(int v) {}
};
struct A
{
operator int() const { return 1; }
operator B() const { return B(); }
};
int main()
{
A a;
static_cast<B>(a); // Error here
a.operator B(); // This is OK
return 0;
}
它会产生这样的编译错误:
main.cpp: In function ‘int main()’:
main.cpp:16:21: error: call of overloaded ‘B(A&)’ is ambiguous
static_cast<B>(a);
^
main.cpp:4:5: note: candidate: B::B(int)
B(int v) {}
^
main.cpp:1:8: note: candidate: constexpr B::B(const B&)
struct B
^
main.cpp:1:8: note: candidate: constexpr B::B(B&&)
我不问如何解决这个问题。 只想了解为什么编译器不接受它? 从我的POV static_cast<B>(a)
等于a.operator B()
但似乎编译器以不同的方式读取它。
更新:
这种行为发生在c ++ 17之前。 使用c ++ 17,此代码不会产生任何编译错误。
这是因为在C ++ 17和static_cast<B>(a);
, direct initialization
的规则(特别是wrt copy elision
)略有改变static_cast<B>(a);
只是导致:
a.operator B();
如cppreference / direct_initialization所述 :
如果初始化程序是一个prvalue表达式,其类型与T相同(忽略cv-qualification),则初始化程序表达式本身(而不是从中实现的临时表达式)用于初始化目标对象:请参阅copy elision(自C + +17)
当在cppreference / copy_elision中进一步阅读时,它表示自C ++ 17以来,编译器需要省略复制和移动构造。
在函数调用中,如果return语句的操作数是prvalue,并且函数的返回类型与该prvalue的类型相同。
因此,while static_cast<B>(a);
pre C ++ 17可以解释为B(a.operator int())
和a.operator B()
,C ++ 17必须选择第二个选项,因为a.operator B()
的返回值是类型B
prvalue
,它可以省略复制/移动构造。
根据N3797 [expr.static.cast]第4段:
表达式
e
可以显式转换到类型T
使用static_cast
形式static_cast<T>(e)
,如果声明T t(e);
对于一些发明的临时变量t
(8.5),其形式良好。 这种显式转换的效果与执行声明和初始化,然后使用临时变量作为转换结果相同。
表达式static_cast<B>(a)
从初始化程序a
执行类型B
的临时变量的直接初始化。 然后N3797 [dcl.init]第17段的以下子弹适用:
如果初始化是直接初始化,或者它是复制初始化,其中源类型的cv-nonqualified版本与目标类相同的类或派生类,则考虑构造函数。 列举了适用的构造函数(13.3.1.3),并通过重载解析(13.3)选择最佳构造函数。 调用所选的构造函数来初始化对象,初始化表达式或表达式列表作为其参数。 如果没有构造函数适用,或者重载决策是不明确的,则初始化是错误的。
“适用的构造函数”由N3797 [over.match.ctor]定义:
对于直接初始化,候选函数是正在初始化的对象的类的所有构造函数。
因此所有三个构造函数: B::B(int)
, B::B(const B&)
, B::B(B&&)
是重载B::B(B&&)
期间的候选者。 然后比较三个对应的隐式转换序列: A->int
, A->const B&
, A->B&&
。 因此,由于N3797 [over.ics.rank]第3段中的以下子弹的条件未得到满足,因此A->int
可与其他两个区分:
用户定义的转换序列
U1
是比另一个用户定义的转换序列U2
更好的转换序列,如果它们包含相同的用户定义的转换函数或构造函数,或者它们在聚合初始化中初始化相同的类,并且在任何一种情况下都是第二个标准转换序列U1优于U2
的第二标准转换序列。
此外,在N3797 [over.match.best]第1段中没有确定最佳可行函数的特殊规则,因此重载决策是不明确的,这会导致编译器错误。
上面的推论也有,所以这是一个编译器错误(目前)。 由于CWG 242 ,[expr.static.cast]第4段的措辞已经改变,但它不会影响我们的结论。
注意保证副本省略不适用于此,因为它不是来自相同类型的prvalue的初始化。
已经有关于在这种直接初始化情况下添加保证副本省略的讨论 ,因此选择a.operator B()
的行为可能在将来是合法的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.