C++,bool 转换是否总是回退到 void* 的隐式转换?

C++, does bool conversion always fall back to implicit conversion to void*?

问题: 隐式 bool 转换是否总是退回到尝试隐式转换为 void*? (如果该类型存在这样的转换函数)。如果是,为什么?

考虑以下短程序:

#include <iostream>

class Foo{
public:

    operator void*() const
    {
        std::cout << "operator void*() const" << std::endl;
        return 0;
    }
};

int main()
{
    Foo f;

    if(f)
        std::cout << "True" << std::endl;
    else
        std::cout << "False" << std::endl;

    return 0;
}

这个程序的输出是:

operator void*() const
False

意思是,调用了void*的转换函数。 如果我们在转换函数前面标记一个 explicit 限定符,那么到 void* 的隐式转换将失败。

编辑: 似乎很多答案都是"null pointers can be converted to false"。我明白这一点,我的问题是关于 "if I can't directly call operator bool() then I will try conversion to any pointer".

这可以是可以在布尔上下文中使用的任何类型,void * 在这里并不特殊,嗯?

任何整数转换 operator 的工作方式相同。你 return 0 在你的运营商中,因此 False.

operator [int,double,uint64_t,<any_integral>]() const
{
    std::cout << "integral operator called" << std::endl;
    return 0;
}

逻辑表达式中可以使用任何整数类型。

真正发生的是,在这种情况下,您的 class 隐式转换为指针类型 void*。你return0,也就是NULL宏,接受为指针类型

指针隐式转换为布尔值,空指针转换为 false。

真的,您可以对 Foo:

的指针进行不同的隐式转换
operator int*() const
{
    std::cout << "operator int* const" << std::endl;
    return new int(3);
}

你的输出将变为

operator int* const
True

但是,如果您有 两者,则会出现编译器错误:

class Foo{
public:

    operator int*() const
    {
        std::cout << "operator int* const" << std::endl;
        return new int(3);
    }
    operator void*() const
    {
        std::cout << "operator void*() const" << std::endl;
        return 0;
    }
};

main.cpp:26:9: error: conversion from 'Foo' to 'bool' is ambiguous

但是,如果您显式定义一个转换,太布尔,那么它就没有歧义:

operator void*() const
{
    std::cout << "operator void*() const" << std::endl;
    return 0;
}

operator bool() const
{
     std::cout << "operator bool() const" << std::endl;
    return true;
} // <--- compiler chooses this one

implicit conversions的主题实际上很有趣,因为它反映了编译器如何为给定的参数(值转换、整数提升等)选择合适的成员函数。

也就是说,编译器有一个优先列表,它会在尝试确定您的意思时从中选择。如果两个重载具有相同的优先级,则会出现错误。

例如,operator bool 将始终被选中,但如果您不得不从 operator intoperator void* 中进行选择,那么 operator int 将被选中,因为它选择数字转换而不是指针转换。

但是,如果您同时拥有 operator intoperator char,那么您会得到一个错误,因为它们都是数字整数转换。

如果编译器无法将用户定义的类型直接转换为 bool,则会尝试间接进行,即将(通过用户定义的转换)转换为可以转换为 bool 而不涉及另一个用户定义的转换。此类类型的列表包括(并且似乎仅限于)以下类型:

  • 整数运算类型(charint 等)
  • 浮点运算类型(float,double,long double)
  • 指针类型(void*属于此处,但也可以是const std::vector<Something>*
  • 指向函数的指针(包括指向成员函数的指针)
  • 对上述任何一种的引用类型

但是请注意,只能存在一个这样的间接转换。如果上述列表中的两个或多个转换是可能的,那么编译器将面临歧义并报告错误。

标准中的一些参考资料:

  • §6.4.0.4 [stmt.select]

    The value of a condition that is an expression is the value of the expression, contextually converted to bool for statements other than switch

  • §4.0.4 [转化]

    Certain language constructs require that an expression be converted to a Boolean value. An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t.

  • §8.5.17 [dcl.init]

    The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression.

  • §8.5.17.7 [dcl.init]

    Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed.

  • §13.3.1.5 [over.match.conv]

    Assuming that “cv1 T is the type of the object being initialized, and “cv S is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

    The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T with a qualification conversion (4.4) are also candidate functions.

  • §4.13.1 [conv.bool]

    A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true.