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

时间:2016-07-22 21:10:50

标签: c++ implicit-conversion void-pointers

问题:隐式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*将会失败。

修改 似乎很多答案是“空指针可以转换为false”。我理解这一点,我的问题是“如果我不能直接拨打operator bool(),那么我会尝试转换为任何指针”。

5 个答案:

答案 0 :(得分:16)

如果编译器无法直接将用户定义的类型转换为bool,那么它会尝试间接地执行此操作,即将(通过用户定义的转换)转换为可转换为{{1不涉及另一个用户定义的转换。这些类型的列表包括(并且似乎仅限于)以下类型:

  • 整数算术类型(boolchar等)
  • 浮点算术类型(intfloatdouble
  • 指针类型(long double属于此处,但它也可以是void*
  • 指向函数的指针(包括指向成员函数的指针)
  • 上述任何一种
  • 的参考类型

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

答案 1 :(得分:6)

对于标准中的一些参考文献:

  • §6.4.0.4[stmt.select]

      

    作为表达式的条件的值是的值   表达式,上下文转换为switch以外的语句e

  • §4.0.4[conv]

      

    某些语言结构要求将表达式转换为布尔值。在这样的上下文中出现的表达式bool被称为在上下文中转换为bool t(e); ,并且当且仅当声明t很好时才是格式良好的形成,对于一些发明的临时变量T

  • §8.5.17[dcl.init]

      

    初始化器的语义如下。 目标类型是要初始化的对象或引用的类型,源类型是初始化表达式的类型。

  • §8.5.17.7[dcl.init]

      

    否则,如果源类型是(可能是cv限定的)类类型,则考虑转换函数。列举了适用的转换函数(13.3.1.5),并通过重载决策(13.3)选择最佳函数。调用如此选择的用户定义转换以将初始化表达式转换为正在初始化的对象。如果无法进行转换或模糊不清,则初始化结构不正确。

  • §13.3.1.5[over.match.conv]

      

    假设“cv1 S是要初始化的对象的类型,“cv S是该类型的初始化表达式,带有S a   类类型,候选函数选择如下:

         

    考虑S及其基类的转换函数。那些未隐藏在T和yield类型T中的非显式转换函数或可通过标准转换序列转换为类型S的类型(13.3.3.1.1)是候选职能。对于直接初始化,那些未隐藏在T和yield类型T中的显式转换函数或者可以转换为带有限定转换(4.4)的bool类型的类型也是候选职能。

  • §4.13.1[conv.bool]

      

    算术,无范围枚举,指针或指向成员类型的指针的prvalue可以转换为false类型的prvalue。零值,空指针值或空成员指针值将转换为true;   任何其他值都将转换为SELECT s.IDST , s.Store, ... LEFT OUTER JOIN dbo.Stores AS s ON s.IDST = A.IDST AND s.IDST = B.IDST AND s.IDST = F.IDST AND s.IDST = PT.IDST AND s.IDST = RP.IDST AND s.IDST = RS.IDST AND s.IDST = ST.IDST

答案 2 :(得分:4)

真正发生的是,在这种情况下,您的类会隐式转换为指针类型void*。您返回0,这是NULL宏,它被接受为指针类型。

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

实际上,您可以对Foo的指针进行不同的隐式转换:

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

您的输出将更改为

  

operator int * const
  真

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

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:错误:转换为&#39; Foo&#39;到了布尔&#39;含糊不清

但是,如果你明确定义了一个转换,那么它也不是模糊的:

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,那么您会收到错误,因为它们都是数字整数转换。

答案 3 :(得分:2)

任何整数转换operator的工作方式都相同。您在运算符中返回0,因此False

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

任何整数类型都可以用于逻辑表达式。

答案 4 :(得分:0)

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