我有一些像这样的代码
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,此代码不会产生任何编译错误。
答案 0 :(得分:2)
这是因为direct initialization
(特别是wrt copy elision
)的规则在C ++ 17中略有改变,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的类型相同。
因此,虽然static_cast<B>(a);
预C ++ 17可以解释为B(a.operator int())
和a.operator B()
,但C ++ 17必须选择第二个选项,因为{的返回值{ {1}}是a.operator B()
类型的prvalue
,它可以省略复制/移动构造。
答案 1 :(得分:2)
根据N3797 [expr.static.cast]第4段:
如果声明
e
格式正确,则可以使用T
形式的static_cast
将表达式static_cast<T>(e)
显式转换为T t(e);
类型,对于一些发明的临时变量t
(8.5)。这种显式转换的效果与执行声明和初始化,然后使用临时变量作为转换结果相同。
表达式static_cast<B>(a)
从初始化程序B
执行a
类型的临时变量的直接初始化。然后来自N3797 [dcl.init]第17段的以下子弹适用:
如果初始化是直接初始化,或者它是复制初始化,其中源类型的cv-nonqualified版本与目标类相同,或者是派生类,则考虑构造函数。 枚举适用的构造函数(13.3.1.3),并通过重载解析(13.3)选择最佳构造函数。调用所选的构造函数以初始化对象,使用初始化表达式或表达式列表作为其参数。如果没有构造函数适用,或者重载决策不明确,则初始化是不正确的。
和&#34;适用的构造者&#34;由N3797 [over.match.ctor]定义:
对于直接初始化,候选函数是正在初始化的对象类的所有构造函数。
因此,所有三个构造函数:B::B(int)
,B::B(const B&)
,B::B(B&&)
在重载解析期间都是候选者。然后比较三个相应的隐式转换序列:A->int
,A->const B&
,A->B&&
。因此,A->int
可与其他两个区别开,因为N3797 [over.ics.rank]第3段中的以下子弹的条件未得到满足:
如果它们包含相同的用户定义转换函数或构造函数,则用户定义的转换序列
U1
是比其他用户定义的转换序列U2
更好的转换序列它们在聚合初始化中初始化相同的类,在任何一种情况下都是第二个标准转换 U1的序列优于U2
的第二标准转换序列。
此外,在N3797 [over.match.best]第1段中没有确定最佳可行功能的特殊规则,因此重载分辨率不明确,导致编译器错误。
上面的推论也有,所以这是一个编译器错误(目前)。由于CWG 242,[expr.static.cast]第4段的措辞已经改变,但这并不影响我们的结论。
注意保证副本省略不适用于此处,因为它不是来自相同类型的prvalue的初始化。
已经有discussion关于为这种直接初始化案例添加有保证的副本省略,因此选择a.operator B()
的行为将来可能是合法的。