下面的代码段取消引用了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类型的类型:标准转换序列(intbool);按照[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/

    10-11 22:06