15

为什么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 integerif语句中被完全识别为bool. (海合会)

4

6 回答 6

17

语法switch ( condition ) statement

条件 - 整数或枚举类型的任何表达式,或上下文 隐式可转换为整数或枚举类型的类类型,或使用大括号或等号初始化器声明此类类型的单个非数组变量。

取自cppreference。

这意味着您只能在 integer 或 enum类型上执行 switch case 。为了使编译器能够将 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”的模棱两可的默认类型转换</p>

开关 (w) {

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

包括 'Wrapper::operator int()' 和 'Wrapper::operator bool()'</p>

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

如果只有一个运算符是显式的,则将为 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 语句都为真,因为根据上下文对 if 语句内的布尔值w进行评估。

于 2018-06-06T12:41:24.873 回答
10

我认为解释了为什么该switch声明不被接受,而该if声明是:

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

  • 控制 if、while、for 的表达式;
  • 逻辑运算符 !、&& 和 ||;
  • 条件运算符 ?:;
  • 静态断言;
  • 没有例外。
于 2018-06-06T12:43:20.137 回答
3

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

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

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

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

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

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

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

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

于 2018-06-06T16:50:39.953 回答
3

一个答案是,if并且switch行为不同,因为这就是标准的编写方式。另一个答案可能会推测为什么标准是这样编写的。好吧,我认为标准if声明的行为方式是解决特定问题(隐式转换为bool 有问题),但我想采用不同的观点。

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

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

于 2018-06-06T16:22:01.220 回答
0

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

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

于 2018-06-06T12:56:27.377 回答
0

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

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

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

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

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

与 不同, C 的switch语句if需要处理离散值,因此它不能如此宽松。

于 2018-06-06T20:43:49.203 回答