【问题标题】:Why do switch and if statements behave differently with conversion operators?为什么 switch 和 if 语句与转换运算符的行为不同?
【发布时间】:2018-06-06 12:29:37
【问题描述】:

为什么switchif 语句对转换运算符的行为不同?

struct WrapperA
{
    explicit operator bool() { return false; }    
};

struct WrapperB
{
    explicit operator int() { return 0; }
};

int main()
{
    WrapperA wrapper_a;
    if (wrapper_a) { /** this line compiles **/ }

    WrapperB wrapper_b;
    switch (wrapper_b) { /** this line does NOT compile **/ }
}

编译错误是switch quantity is not an integer,而在if 语句中它被完全识别为bool。 (海合会)

【问题讨论】:

    标签: c++ if-statement switch-statement conversion-operator explicit-conversion


    【解决方案1】:

    语法是switch ( condition ) statement with

    条件 - 整数或枚举类型的任何表达式,或 contextually 隐式 可转换为整数或枚举类型的类类型,或单个非这种类型的数组变量,带有大括号或等号初始值设定项。

    取自cppreference.

    这意味着您只能对整数或枚举类型进行切换。为了使编译器能够将 Wrapper 隐式转换为整数/枚举类型,您需要删除显式关键字:

    显式说明符指定构造函数或转换函数(C++11 起)不允许隐式转换

    您也可以将 Wrapper 转换为 int 类型。

    编辑地址@acraig5075 评论:

    您必须注意哪个运算符是显式的,哪个是隐式的。如果两者都是隐式的,则代码将无法编译,因为会有歧义:

    struct Wrapper
    {
        operator int() { return 0; }
        operator bool() { return true; }    
    };
    

    source_file.cpp:在函数“int main()”中:source_file.cpp:12:14:

    错误:'Wrapper' 的默认类型转换不明确

    开关 (w) {

    ^ source_file.cpp:12:14: 注意:候选转换

    包括‘Wrapper::operator int()’和‘Wrapper::operator bool()’

    消除歧义的唯一方法是进行强制转换。

    如果只有一个运算符是显式的,则另一个运算符将被选择用于 switch 语句:

    #include <iostream>
    struct Wrapper
    {
        explicit operator int() { return 0; }
        operator bool() { return true; }    
    };
    
    int main()
    {
        Wrapper w;
        if (w) { /** this line compiles **/std::cout << " if is true " << std::endl; }
        switch (w) { 
            case 0:
                std::cout << "case 0" << std::endl;
                break;
            case 1:
                std::cout << "case 1" << std::endl;
                break;
        }
        return 0;
    }
    

    输出:

     if is true 
    case 1
    

    w 已隐式转换为 1 (true)(因为 operator int 是显式的)并执行 case 1。

    另一方面:

    struct Wrapper
    {
        operator int() { return 0; }
        explicit operator bool() { return true; }    
    };
    

    输出:

     if is true 
    case 0
    

    w 已隐式转换为 0,因为运算符 bool 是显式的。

    在这两种情况下,if 语句都是正确的,因为 w 在 if 语句中被评估为 上下文 为布尔值。

    【讨论】:

    • 但是,如果 bool 和 int 强制转换都存在,即使两者都没有明确声明,仍然会出现歧义错误。
    • 我添加了一个例子来解决歧义部分
    • switch 隐式转换为 int,而 if 进行显式转换(到 bool)的 2 美分总结也是如此吗?
    • 基本上,是的。编译器将 if 语句中的条件转换为布尔值。这就是为什么if(...) 没有歧义,即使两者都是隐式运算符。
    【解决方案2】:

    我认为this 解释了为什么不接受switch 语句,而if 语句是:

    在以下五种上下文中,如果声明 bool t(e); 格式正确,则需要类型 bool 并构建隐式转换序列。即考虑explicit T::operator bool() const;等显式用户自定义转换函数。据说这样的表达式 e 可以在上下文中转换为 bool

    • 控制 if、while、for 的表达式;
    • 逻辑运算符 !、&& 和 ||;
    • 条件运算符 ?:;
    • static_assert;
    • 没有例外。

    【讨论】:

    • 这解释了为什么if 被接受,但没有解释为什么在switch 语句的上下文中,相同的“上下文可转换”概念被决定不适用于整数类型.
    • 当它引用后面的 3 个上下文时继续阅读(包括 switch):Note that explicit conversion functions are not considered, even though they are considered in contextual conversions to bool. 但是我没有找到确切的原因为什么没有使用 switch 实现。跨度>
    • 这里的“双重标准”(可以这么说)让我很困惑。
    • @nyarlathotep108: explicit 转换运算符的存在是为了防止人们执行隐式转换。 “上下文转换为bool”规则的唯一目的是在某些非常特定的位置排除此规则。它仅适用于bool,并且仅适用于这些位置,按设计。这使我们可以避免需要“安全布尔”习语,同时仍然能够拥有可转换为 bool 条件的类型。
    • @nyarlathotep108 C++ 中没有其他东西可以提供对任何整数类型的自动/隐式强制转换。考虑一个包含三个返回不同枚举类型的转换运算符的类,隐式切换转换应该更喜欢这三个中的哪一个?
    【解决方案3】:

    声明转换运算符explicit 是为了防止隐式转换为该类型。这就是它的目的。 switch 尝试将其参数隐式转换为整数;因此不会调用explicit 运算符。这是预期的行为。

    出乎意料的是explicit 运算符在if 的情况下被调用。从而挂了一个故事。

    请看,鉴于上述规则,通过if 使类型可测试的方法是将非explicit 转换为bool。问题是……bool 是一个有问题的类型。它可以隐式转换为整数。因此,如果您创建一个可隐式转换为 bool 的类型,则这是合法代码:

    void foo(int);
    foo(convertible_type{});
    

    但这也是无意义的代码。你从来没有打算让convertible_type 隐式转换为整数。您只是希望将其转换为布尔值以进行测试。

    Pre-C++11,解决这个问题的方法是“安全布尔成语”,这是一种复杂的痛苦并且没有逻辑意义(基本上,您提供了对成员指针的隐式转换,该指针可以转换为一个布尔值,但不是整数或常规指针)。

    所以在 C++11 中,当他们添加explicit 转换运算符时,他们为bool 做了一个例外。如果您有explicit operator bool(),则此类型可以在一组语言定义的位置内“根据上下文转换为bool”。这允许explicit operator bool() 表示“可在布尔条件下测试”。

    switch 不需要这种保护。如果您希望某个类型可以隐式转换为 int 以达到 switch 的目的,那么它也没有理由不能隐式地转换为 int 以用于其他目的。

    【讨论】:

      【解决方案4】:

      一个答案是ifswitch 的行为不同,因为这就是标准的编写方式。另一个答案可能会推测为什么标准是这样编写的。好吧,我想标准制定的if 语句的行为方式是解决特定问题(隐式转换为bool had been problematic),但我想采用不同的观点。

      if 语句中,条件必须是布尔值。不是每个人都以这种方式考虑if 语句,大概是因为语言内置的各种便利性。然而,在其核心,if 语句需要知道“做这个”或“做那个”; “是还是不是”; truefalse - 即布尔值。在这方面,将某些内容放在语句的条件中是明确要求将某些内容转换为bool

      另一方面,switch 语句接受任何整数类型。也就是说,没有任何一种类型优于所有其他类型。使用switch 可以被视为将值转换为整数类型的显式请求,但不一定专门针对int。因此,当需要明确请求特定转换时,不适合使用到 int 的转换。

      【讨论】:

        【解决方案5】:

        我相信这种行为的真正原因源于 C,但在解释之前,我将尝试用 C++ 术语来证明它的合理性。

        if/while/for 语句应该采用任何标量(整数、浮点数或指针)或可转换为 bool 的类实例。它只是检查该值是否等于零。鉴于这种许可性,编译器使用explicit 运算符将值放入if 语句中相对无害。

        另一方面,switch 只有整数和enums 才真正有意义。如果您尝试将switchdouble 或指针一起使用,则会出现相同的错误。这使得定义离散案例值变得更加容易。由于switch 语句特别需要查看整数,因此使用未定义隐式转换的类实例可能是错误的。

        从历史上看,原因是 C 就是这样做的。

        C++ 最初旨在与 C 向后兼容(尽管它从未成功过)。直到最近,C 才具有布尔类型。 C 的if 语句必须是允许的,因为没有其他方法可以做到这一点。虽然 C 不像 C++ 那样进行结构到标量的类型转换,但 C++ 对explicit 方法的处理反映了if 语句在两种语言中都非常宽松的事实。

        C 的switch 语句,与if 不同,需要处理离散值,因此不能这么随意。

        【讨论】:

        • 这个答案关于 C 是不准确的。在 C 中,if 语句的条件必须是标量类型(不是类实例——C 没有“可转换”的概念)。因此,这个涉及可转换为标量类型的结构的特定问题不适用于 C;即 C++ 可以以任何它喜欢的方式处理它,并且仍然保持与 C 的兼容性。无论做什么,它都不会是 C 的方式。事实上,直到 C++11,使用显式转换甚至不是 C++ 处理if 语句的方式。
        • 我没有说 C 在 if 语句中进行转换。我试图解释为什么问题中描述的行为正是考虑到浮点数和指针在两种语言中的处理方式所期望的。 C 在这里适用,因为这是开创先例的语言。作为在 C++ 之前学习 C 的人,对我来说这似乎是 OP 正在寻找的原因。
        • “明显”这个词被过度使用了。例如,考虑到浮点数和指针在两种语言中的处理方式,该行为不是 I 所期望的。作为在 C++ 之前学习 C 的人,在我看来这些结论是错误的,因此 不是 OP 正在寻找的东西。
        • 澄清一下:if 语句的行为不是我所期望的。如果只谈论switch 语句的行为,那么可以肯定的是,这两种语言都是一样的。但这并没有真正解决为什么if 会查看显式转换而switch 不会的问题。
        • 另一个澄清:我对为什么这种行为是合理的理由没有任何疑问。我质疑的是(粗体字)断言“这就是 C 的方式”。从我的角度来看,如果不提及 C,这个答案会很好。
        【解决方案6】:

        您的代码有两个问题。 首先,转换运算符不能显式地与switch 语句一起使用。 其次,switch 条件需要整数类型,而intbool 都是此类类型,因此存在歧义。 如果您更改您的类以使其不存在这两个问题,switch 将按预期编译和工作。

        另一个不需要更改类的解决方案是将值显式转换为(static_cast 可以)intbool

        【讨论】:

        • 我正在重新表述问题,因为示例代码在问题本身的目的上引起了歧义
        • 在您重新提出问题后:关于switch,没有歧义,但转换运算符是显式的,因此它不起作用,因为switch 要求条件表达式可以隐式转换为一个整数类型。对于if,它的条件是这样的上下文,编译器也会考虑到显式转换为bool,所以没问题。换句话说,switch 需要整数类型,并且只能使用隐式转换,而在 if 语句中需要 bool,但可以使用隐式和显式转换。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 2019-07-04
        • 1970-01-01
        • 1970-01-01
        • 2015-07-20
        相关资源
        最近更新 更多