繁体   English   中英

调用重载的static_cast是不明确的

[英]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 ,它可以省略复制/移动构造。

C ++ 14

根据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->intA->const B&A->B&& 因此,由于N3797 [over.ics.rank]第3段中的以下子弹的条件未得到满足,因此A->int可与其他两个区分:

用户定义的转换序列U1是比另一个用户定义的转换序列U2更好的转换序列,如果它们包含相同的用户定义的转换函数或构造函数,或者它们在聚合初始化中初始化相同的类,并且在任何一种情况下都是第二个标准转换序列U1优于U2的第二标准转换序列。

此外,在N3797 [over.match.best]第1段中没有确定最佳可行函数的特殊规则,因此重载决策是不明确的,这会导致编译器错误。

C ++ 17

上面的推论也有,所以这是一个编译器错误(目前)。 由于CWG 242 ,[expr.static.cast]第4段的措辞已经改变,但它不会影响我们的结论。

注意保证副本省略不适用于此,因为它不是来自相同类型的prvalue的初始化。

C ++ 20或更高版本

已经有关于在这种直接初始化情况下添加保证副本省略的讨论 ,因此选择a.operator B()的行为可能在将来是合法的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM