为什么switch和if语句与转换运算符的行为不同?

时间:2018-06-06 12:29:37

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

为什么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。 (GCC)

6 个答案:

答案 0 :(得分:17)

语法为switch ( condition ) statement

  

condition - 任何整数或枚举类型的表达式,或类类型上下文 隐式可转换为整数或枚举类型,或单个非声明这种类型的数组变量,带有大括号或等号初始化程序。

取自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已隐式转换为1true)(因为operator int是显式的)并且执行了案例1。

另一方面:

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

输出:

 if is true 
case 0

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

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

答案 1 :(得分:10)

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

  

在以下五个上下文中,预期类型为bool,如果声明bool t(e);格式正确,则构建隐式转换序列。也就是说,考虑了显式的用户定义转换函数,例如explicit T::operator bool() const;。这样的表达式e被称为在语境上可转换为bool

  • 控制if,while,for;
  • 的表达式
  • 逻辑运营商!,&amp;&amp;和||;
  • 条件运算符?:;
  • static_assert;
  • noexcept。

答案 2 :(得分:3)

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

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

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

答案 3 :(得分:3)

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

意外的是explicit案例中调用if运算符。从而引发了一个故事。

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

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

但这也是无意义的代码。你永远不会让convertible_type隐式转换为整数。您只是希望它转换为布尔值以进行测试。

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

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

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

答案 4 :(得分:0)

您的代码存在两个问题。 首先,转换运算符不能明确地使用switch语句。 其次,int条件需要一个整数类型,boolswitch都是这样的类型,因此存在歧义。 如果您更改了课程以使其没有这两个问题,则static_cast将按预期编译并正常工作。

另一个不需要更改课程的解决方案是将值intbool显式转换为{{1}}。

答案 5 :(得分:0)

我认为这种行为的真正原因源于C,但在解释之前,我会尝试用C ++术语来证明这一点。

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

另一方面,

switch只对整数和enum有用。如果您尝试将switchdouble或指针一起使用,则会出现相同的错误。这样可以更轻松地定义离散的案例值。由于switch语句特别需要查看整数,因此使用不定义隐式转换的类实例可能是错误的。

历史上,原因是这就是C的用途。

C ++最初的目的是向后兼容C(虽然它从来没有成功过)。直到最近,C才会有布尔类型。 C的if陈述必须是宽容的,因为没有任何其他方式可以做到这一点。虽然C不像C ++那样进行struct-to-scalar类型转换,但C ++对explicit方法的处理反映了if语句在两种语言中都非常宽松的事实。 / p> 与switch不同,C&#39 if语句需要使用离散值,因此它不会那么宽松。