为什么switch
和if
语句与转换运算符的行为不同?
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)
答案 0 :(得分:17)
语法为switch ( condition ) statement
condition - 任何整数或枚举类型的表达式,或类类型上下文 隐式可转换为整数或枚举类型,或单个非声明这种类型的数组变量,带有大括号或等号初始化程序。
这意味着您只能对整数或枚举类型执行切换操作。为了使编译器能够隐式地将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是显式的)并且执行了案例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 。
答案 2 :(得分:3)
一个答案是if
和switch
表现不同,因为这就是标准的编写方式。另一个答案可能会推测为什么标准是这样编写的。好吧,我认为标准的if
语句表达了解决特定问题的方式(隐式转换为bool
had been problematic),但我想采用不同的观点。
在if
语句中,条件必须是布尔值。不是每个人都以这种方式考虑if
语句,大概是因为语言中内置了各种便利。但是,if
语句的核心是需要知道“做这个”或“做那个”; “是还是不是”; true
或false
- 即布尔值。在这方面,将某些内容置于语句的条件内是明确要求将某些内容转换为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
条件需要一个整数类型,bool
和switch
都是这样的类型,因此存在歧义。
如果您更改了课程以使其没有这两个问题,则static_cast
将按预期编译并正常工作。
另一个不需要更改课程的解决方案是将值int
或bool
显式转换为{{1}}。
答案 5 :(得分:0)
我认为这种行为的真正原因源于C,但在解释之前,我会尝试用C ++术语来证明这一点。
if
/ while
/ for
语句应该采用任何标量(整数,浮点数或指针)或可转换为bool
的类实例。它只是检查该值是否等于零。鉴于这种放宽性,编译器使用explicit
运算符将值拟合到if
语句中相对无害。
switch
只对整数和enum
有用。如果您尝试将switch
与double
或指针一起使用,则会出现相同的错误。这样可以更轻松地定义离散的案例值。由于switch
语句特别需要查看整数,因此使用不定义隐式转换的类实例可能是错误的。
历史上,原因是这就是C的用途。
C ++最初的目的是向后兼容C(虽然它从来没有成功过)。直到最近,C才会有布尔类型。 C的if
陈述必须是宽容的,因为没有任何其他方式可以做到这一点。虽然C不像C ++那样进行struct-to-scalar类型转换,但C ++对explicit
方法的处理反映了if
语句在两种语言中都非常宽松的事实。 / p>
与switch
不同,C&#39 if
语句需要使用离散值,因此它不会那么宽松。