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

Posted

技术标签:

【中文标题】C++,bool 转换是不是总是退回到 void* 的隐式转换?【英文标题】:C++, does bool conversion always fall back to implicit conversion to void*?C++,bool 转换是否总是退回到 void* 的隐式转换? 【发布时间】:2016-11-26 21:06:37 【问题描述】:

问题: 隐式 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() 那么我将尝试转换为任何指针”。

【问题讨论】:

“将尝试转换为任何指针” - void 指针不是“任何”指针。 void 指针是 void 指针,仅此而已。它只接受任何类型的地址。 @BlackMoses 我的意思是任何指针类型,鉴于接受的答案似乎确实如此。 @JesperJuhl 这不是一个愚蠢的问题。它询问是否允许编译器执行此操作,以及其背后的原因。这对您来说可能相当明显,但对于初学者到中级开发人员来说,这可能会让人感到困惑。 【参考方案1】:

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

【讨论】:

【参考方案2】:

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

operator [int,double,uint64_t,<any_integral>]() const

    std::cout << "integral operator called" << std::endl;
    return 0;

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

【讨论】:

【参考方案3】:

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

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

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

operator int*() const

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

你的输出会变成

运算符 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: 错误:从 'Foo' 到 'bool' 的转换不明确

然而,如果你明确定义了一个转换,也就是 bool,那么它就没有歧义了:

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

【讨论】:

谢谢你,一个很好的答案。但它并没有真正回答我关于为什么 C++ 会“回退”并尝试将我的对象转换为任何指针类型的问题? @jensa:查看我的编辑以获得更全面的解释。【参考方案4】:

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

整数算术类型(charint 等) 浮点算术类型(floatdoublelong double指针类型(void* 属于这里,但也可以是 const std::vector&lt;Something&gt;* 指向函数的指针(包括指向成员函数的指针) 上述任何一种的引用类型

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

【讨论】:

This example 看起来很矛盾,它适用于 clang 和 gcc。从标准来看,我希望两个转换运算符都会触发错误。也许标准中的某处说明了对数字(非指针)类型的偏好。【参考方案5】:

标准中的一些参考:

§6.4.0.4 [stmt.select]

作为表达式的条件的值是 表达式,根据上下文转换为 bool 用于 switch 以外的语句

§4.0.4 [转换]

某些语言结构要求将表达式转换为布尔值。出现在这种上下文中的表达式 e 被称为在上下文中转换为 bool 并且当且仅当声明 bool t(e); 是格式正确的,对于某些发明的临时变量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 T是被初始化对象的类型,“cv S是初始化表达式的类型,有萨 类类型,候选函数选择如下:

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

§4.13.1 [conv.bool]

算术、无范围枚举、指针或指向成员类型的指针的纯右值可以转换为bool 类型的纯右值。零值、空指针值或空成员指针值转换为false; 任何其他值都将转换为true

【讨论】:

以上是关于C++,bool 转换是不是总是退回到 void* 的隐式转换?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 std::fstream 返回 void 而不是 bool

void QWebView::loadFinished(bool ok) ok 总是假的

如何判断 DXVA 解码器是不是已退回到软件解码

公共 bool 方法不能将类型“bool”隐式转换为“void”

无法将类型“void”隐式转换为“bool”Xamarin 警报

当远程但不是本地时,Socket.io 退回到 nginx 代理后面的轮询(websocket 给出 400)