下面的代码段取消引用了nullptr
。
struct Foo {
int *bar;
operator int&() {
return *bar;
}
explicit operator bool() const {
return bar;
}
};
int main() {
Foo f {nullptr};
auto _ = bool(f);
}
bool(f)
调用bool(f.operator int&())
而不是按预期的方式调用f.operator bool()
? bool(f)
按预期的方式调用f.operator bool()
而不将operator int&
标记为explicit
? 最佳答案
在用户定义的转换的过载解析中,参数转换排名优先于返回类型转换排名
以下所有标准引用均引用N4659: March 2017 post-Kona working draft/C++17 DIS。
准备工作
为避免必须处理段错误,减少auto
类型并考虑标记了explicit
的转换函数,请考虑以下示例的简化变体,该变体具有相同的行为:
#include <iostream>
struct Foo {
int bar{42};
operator int&() {
std::cout << __PRETTY_FUNCTION__;
return bar;
}
operator bool() const {
std::cout << __PRETTY_FUNCTION__;
return true;
}
};
int main() {
Foo f {};
// (A):
bool a = f; // Foo::operator int&()
}
TLDR
初始化转换序列的可行候选函数为
operator int&(Foo&)
operator bool(const Foo&)
和[over.match.best]/1.3(在函数参数上排名)优先于[over.match.best]/1.4(在从返回类型转换时排名),这意味着operator int&(Foo&)
会被[over.match.best]选为Foo&
类型的参数,因为它是毫无歧义的完美匹配。 ]/1.3规则,而operator bool(const Foo&)
则不是。在这方面,因为我们依赖[over.match.best]/1.3,所以它与仅重载
const
-qualification只是简单地没有什么不同:#include <iostream>
struct Foo {};
void f(Foo&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
void f(const Foo&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
int main() {
Foo f1 {};
const Foo f2{};
f(f1); // void f(Foo&)
f(f2); // void f(const Foo&)
}
如上所述,如果为成员函数重载提供匹配的cv限定词,则根据[over.match.best]/1.3,隐式对象参数将不再有任何区别,并且
bool
转换函数将被选择为根据[over.match.best]/1.4最可行。请注意,通过将int&
转换函数标记为explicit
,它将不再是可行的候选者,并且选择bool
转换函数并不是因为它是最可行的重载,而是因为它是唯一可行的重载。细节
(A)处的表达式是初始化的,其语义具体由[dcl.init]/17.7 [提取,强调我的]所控制:
其中[over.match.conv]/1描述在重载解析中哪些转换函数被视为候选函数:
在此示例中,cv
T
(正在初始化的对象的类型)是bool
,因此,两个已使用的转换函数都是可行的候选方法,因为一个函数直接产生bool
类型,另一个产生可以通过以下方式转换为bool
类型的类型:标准转换序列(int
到bool
);按照[conv.bool]:此外,初始化器表达式的类型为
Foo
,并且[over.match.funcs]/4决定用户定义的转换函数的隐式对象参数的类型的cv限定是相应函数的cv限定:因此,重载解决方案我们可以总结如下:
// Target type: bool
// Source type: Foo
// Viable candidate functions:
operator int&(Foo&)
operator bool(const Foo&)
在继续进行过载解析如何选择最佳可行候选时,我们在不失一般性的情况下(根据[over.match.funcs]/5)将隐含对象参数添加为显式函数参数。现在,[over.ics.user],尤其是[over.ics.user]/2对此进行了总结:
特别是选择最佳可行候选人的规则受[over.match.best]约束,尤其是[over.match.best]/1:
这里的关键是[over.match.best]/1.4关于候选者返回类型的转换(转换为目标类型)仅在无法通过[over.match.best]/1.3消除过载的情况下适用。 。但是,在我们的示例中,请记住可行的候选函数是:
operator int&(Foo&)
operator bool(const Foo&)
根据[over.ics.rank]/3.2,尤其是[over.ics.rank]/3.2.6:意思是,对于
Foo&
类型的参数,operator int&(Foo&)
将是更好的(/1.3:精确)匹配,而对于例如类型为const Foo&
的参数,operator bool(const Foo&)
将是唯一的匹配项(Foo&
将不可行)。关于c++ - 为什么bool(val)比val.operator bool()更喜欢双隐式转换?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/64838687/