我最近失去了一些时间来弄清楚我的代码中的错误是由一个错字引起的:
if(a=b)
Run Code Online (Sandbox Code Playgroud)
代替:
if(a==b)
Run Code Online (Sandbox Code Playgroud)
我想知道是否有任何特殊情况你想要在if语句中为变量赋值,或者如果没有,为什么编译器不会发出警告或错误?
Lig*_*ica 39
if (Derived* derived = dynamic_cast<Derived*>(base)) {
// do stuff with `derived`
}
Run Code Online (Sandbox Code Playgroud)
虽然这被称为反模式("使用虚拟调度!"),但有时Derived类型具有Base根本不具备的功能(并因此具有不同的功能),这是切换语义差异的好方法.
cma*_*ter 26
以下是有关语法的一些历史记录.
在经典C中,错误处理经常通过编写如下内容来完成:
int error;
...
if(error = foo()) {
printf("An error occured: %s\nBailing out.\n", strerror(error));
abort();
}
Run Code Online (Sandbox Code Playgroud)
或者,每当有一个可能返回空指针的函数调用时,该习惯用法反过来使用:
Bar* myBar;
... //in old C variables had to be declared at the start of the scope
if(myBar = getBar()) {
//do something with myBar
}
Run Code Online (Sandbox Code Playgroud)
但是,这种语法非常危险
if(myValue == bar()) ...
Run Code Online (Sandbox Code Playgroud)
这就是为什么许多人认为条件不好的情况下的任务,编译器开始警告它(至少有-Wall).但是,可以通过添加一组额外的括号来避免此警告:
if((myBar = getBar())) { //tells the compiler: Yes, I really want to do that assignment!
Run Code Online (Sandbox Code Playgroud)
然后C99出现了,允许你混合定义和语句,所以许多开发人员会经常写类似的东西
Bar* myBar = getBar();
if(myBar) {
Run Code Online (Sandbox Code Playgroud)
哪个感觉很尴尬.这就是为什么最新标准允许在条件内定义,以提供简短,优雅的方式来执行此操作:
if(Bar* myBar = getBar()) {
Run Code Online (Sandbox Code Playgroud)
这句话中没有任何危险,你明确地给变量一个类型,显然希望它被初始化.它还避免了额外的行来定义变量,这很好.但最重要的是,编译器现在可以轻松捕获这种错误:
if(Bar* myBar = getBar()) {
...
}
foo(myBar->baz); //compiler error
//or, for the C++ enthusiasts:
myBar->foo(); //compiler error
Run Code Online (Sandbox Code Playgroud)
如果没有if语句中的变量定义,则无法检测到此条件.
简而言之:你问的语法是旧C的简单性和强大的产物,但它是邪恶的,所以编译器可以警告它.由于它也是表达常见问题的一种非常有用的方法,因此现在有一种非常简洁,错误的方法来实现相同的行为.它有很多好的,可能的用途.
Mar*_*oun 13
赋值运算符返回指定值的值.所以,我可能会在这样的情况下使用它:
if(x = getMyNumber())
然后我指定x为返回的值,getMyNumber并检查它是否不为零.
避免这样做,我给你举了一个例子,只是为了帮助你理解这一点.
编辑: 添加只是一个建议(可能会喜欢).
为了避免这样的错误 - 一些扩展,应该写条件if(NULL == ptr)而不是if(ptr == NULL)因为当你将相等检查运算符拼错==为运算符时=,编译将抛出一个左值错误if(NULL = ptr),但是if(res = NULL)由编译器传递(这不是你的意思)和仍然是运行时代码中的错误.
出于同样的原因,我宁愿写if(getMyNumber() ==x)而不是if(x == getMyNumber())
人们还应该阅读:对这种代码的批评.
小智 13
在C++17中,可以使用:
if (<initialize> ; <conditional_expression>) { <body> }
Run Code Online (Sandbox Code Playgroud)
类似于for循环迭代器初始值设定项。
这是一个例子:
if (Employee employee = GetEmployee(); employee.salary > 100) { ... }
Run Code Online (Sandbox Code Playgroud)
Adr*_*thy 11
为什么编译器不抛出警告
一些编译器会为条件表达式中的可疑赋值生成警告,尽管您通常必须显式启用警告。
例如,在 Visual C++ 中,您必须启用C4706(或一般的 4 级警告)。我通常会尽可能多地打开警告并使代码更加明确以避免误报。例如,如果我真的想这样做:
if (x = Foo()) { ... }
Run Code Online (Sandbox Code Playgroud)
然后我会写成:
if ((x = Foo()) != 0) { ... }
Run Code Online (Sandbox Code Playgroud)
编译器看到显式测试并假定分配是有意的,因此您不会在此处收到误报警告。
这种方法的唯一缺点是在条件中声明变量时不能使用它。也就是说,你不能重写:
if (int x = Foo()) { ... }
Run Code Online (Sandbox Code Playgroud)
作为
if ((int x = Foo()) != 0) { ... }
Run Code Online (Sandbox Code Playgroud)
从语法上讲,这是行不通的。因此,您要么必须禁用警告,要么对范围的紧密程度做出妥协x。
更新: C++17 添加了在 if 语句 ( p0305r1 )的条件中具有 init 语句的能力,这很好地解决了这个问题(用于比较,而不仅仅是!= 0)。
if (x = Foo(); x != 0) { ... }
Run Code Online (Sandbox Code Playgroud)
此外,如果需要,您可以将范围限制x为仅 if 语句:
if (int x = Foo(); x != 0) { /* x in scope */ ... }
// x out of scope
Run Code Online (Sandbox Code Playgroud)
这取决于您是否要编写干净的代码.当C首次开发时,干净代码的重要性并未得到充分认识,编译器非常简单:使用这样的嵌套赋值通常会导致更快的代码.今天,我想不出一个好的程序员会做的任何情况.它只会使代码的可读性降低,维护起来也更困难.
我遇到了一个案例,该案例最近才有用,所以我认为应该发布它。
假设您要在一个条件中检查多个条件,并且如果其中任何一个条件为真,则您想生成一条错误消息。如果要在错误消息中包括导致错误的特定条件,可以执行以下操作:
std::string e;
if( myMap[e = "ab"].isNotValid() ||
myMap[e = "cd"].isNotValid() ||
myMap[e = "ef"].isNotValid() )
{
// here, e has the key for which the validation failed
}
Run Code Online (Sandbox Code Playgroud)
因此,如果第二个条件是评估为true的条件,则e等于“ cd”。这是由于||标准规定了短路行为(除非过载)。有关短路的更多详细信息,请参见此答案。
| 归档时间: |
|
| 查看次数: |
56944 次 |
| 最近记录: |