在哪种情况下,(a = b)是个好主意?
可能重复:
无意中使用=而不是==
C ++编译器通过您编写的警告让您知道
if( a = b ) { //...
并且这可能是一个错误,你当然想写
if( a == b ) { //...
但有没有一种情况应该忽略警告,因为这是使用这个“function”的好方法? 我没有看到任何代码清晰度可能的原因,那么它有用吗?
两个可能的原因:
-
分配和检查
=
运算符(未覆盖时)通常返回它分配的值。 这是为了允许诸如a=b=c=3
语句。 在您的问题的上下文中,它还允许您执行以下操作:bool global;//a global variable //a function int foo(bool x){ //assign the value of x to global //if x is equal to true, return 4 if (global=x) return 4; //otherwise return 3 return 3; }
…相当于但短于:
bool global;//a global variable //a function int foo(bool x){ //assign the value of x to global global=x; //if x is equal to true, return 4 if (global==true) return 4; //otherwise return 3 return 3; }
此外,应该注意(正如Billy ONeal在下面的评论中所述),当
=
运算符的左侧参数实际上是一个具有为可以被强制的类型指定的转换运算符的类时,这也可以工作(隐式地)转换成一个布尔。 换句话说,如果a是一个可以强制转换为布尔值的类型,则(a=b)
将变为true
或false
。所以以下是与上面类似的情况,除了
=
的左手参数是一个对象而不是一个bool:#include
using namespace std; class Foo { public: operator bool (){ return true; } Foo(){} }; int main(){ Foo a; Foo b; if (a=b) cout<<"true"; else cout<<"false"; } //output: true 注意:在撰写本文时,上面的代码格式会被窃听。 我的代码(检查源代码)实际上具有正确的缩进,移位运算符和行间距。
<
应该是<的,并且每条线之间不应该存在巨大的差距。 -
重写
=
运算符由于C ++允许覆盖运算符,因此有时候=将覆盖除了原始类型之外的其他内容。 在这些情况下,对对象执行
=
操作可能会返回一个布尔值(如果这是为该对象类型覆盖=
运算符的方式)。因此,以下代码将对a执行
=
操作,a
b
作为参数。 然后它会有条件地执行一些代码,具体取决于该操作的返回值:if (a=b){ //execute some code }
在这里,
a
必须是一个对象,b
将是正确的类型,如对于a
类型的对象重写=
运算符所定义的。 要了解有关运算符覆盖的更多信息,请参阅此维基百科文章,其中包含C ++示例: 维基百科有关运算符覆盖的文章
while ( (line = readNextLine()) != EOF) { processLine(); }
您可以用来测试函数是否返回任何错误
if (error_no = some_function(...)) { //handle error }
假设some_function在发生错误时返回错误代码,否则返回零
这是C语言基本function的结果:
赋值操作的值是赋值本身 。
您可以将“返回值”用作if()
语句的条件这一事实是偶然的。
顺便说一句,这是同样的技巧,允许这种疯狂的简洁:
void strcpy(char *s, char *t) { while( *s++ = *t++ ); }
当然,当达到t
的nullchar时,while会退出,但同时会将其复制到目标s
字符串。
这是否是一个好主意,通常不是,因为它降低了代码的可读性并且容易出错。
虽然构造是完全合法的语法,您的意图可能真的如下所示:
if( (a = b) != 0 ) { ... }
不要将“!= 0”部分留下。
6个月,1年,5年后看代码的人乍一看,只是相信代码包含一个由jr写的“经典bug”。 程序员并将尝试“修复”它。 CLEARLY上面的结构表明了你的意图,并将由编译器进行优化。 如果你是那个人,这将特别令人尴尬。
你的另一个选择是重复评论。 但以上是自我记录的代码,这是更好的。
最后,我的偏好是这样做:
a = b; if( a != 0 ) { ... }
这很清楚,因为代码可以获得。 如果性能受到影响,则几乎为零。
void some( int b ) { int a = 0; if( a = b ) { // or do something with a // knowing that is not 0 } // b remains the same }
直接回答这个问题,作为一个个人观点,我真的不认为这是一个好主意的情况。
我的意思是,我知道使用这种语法可以避免在代码中添加额外的行,但我认为它会从代码中消除一些可读性。
这种语法对于像@Steven Schlansker建议的那样非常有用,但直接使用它作为条件并不是一个好主意。
只是我的两分钱……
这实际上不是C的故意特征,而是另外两个特征的结果:
赋值返回指定的值
这对于执行多个赋值(例如a = b = 0
)或循环(例如while ((n = getchar()) != EOF)
非常有用。
数字和指针都有真值
C最初在1999年标准之前没有bool
类型,因此它使用int
来表示布尔值。 向后兼容性要求C和C ++允许if
, while
和for
非bool
表达式。
因此,如果a = b
有一个值,并且if
对它接受的值if
宽松,则if (a = b)
有效。 但我建议使用if ((a = b) != 0)
来阻止任何人“修复”它。
一个有用的常见例子可能是:
do { ... } while (current = current->next);
您应该以更好的编码方式明确地编写检查语句,避免使用assign&check方法。 例:
if ((fp = fopen("filename.txt", "wt")) != NULL) { // do something with fp }
while( (l = getline()) != EOF){ printf("%s\n", l); }
这当然是最简单的例子,有很多次这是有用的。 要记住的主要事情是(a = true)返回true,就像(a = false)返回false一样。
但有没有一种情况应该忽略警告,因为这是使用这个“function”的好方法? 我没有看到任何代码清晰度可能的原因,那么它有用吗?
可以通过在作业周围放置额外的括号来抑制警告。 这样澄清了程序员的意图。 我见过的常见情况与(a = b)情况直接匹配的情况如下:
if ( (a = expression_with_zero_for_failure) ) { // do something with 'a' to avoid having to reevaluate // 'expression_with_zero_for_failure' (might be a function call, eg) } else if ( (a = expression2_with_zero_for_failure) ) { // do something with 'a' to avoid having to reevaluate // 'expression2_with_zero_for_failure' } // etc.
至于编写这种代码是否足够有用来certificate初学者(有时甚至是最糟糕时刻的专业人士)在使用C ++时遇到的常见错误,很难说。 这是inheritance自C和Stroustrup的遗产,其他有助于设计C ++的人可能已经走了一条完全不同的,更安全的路线,如果他们没有尽可能地使C ++向后兼容C语言。
我个人认为这不值得。 我在一个团队工作,之前我曾多次遇到过这个bug。 我会赞成不允许它(至少需要括号或其他一些明确的语法,否则它被认为是构建错误)以换取减轻遇到这些错误的负担。
前言
请注意,这个答案是关于C ++的(我在添加标签“C”之前开始写这个答案)。
在阅读Jens Gustedt的评论之后,我意识到这不是我第一次写这样的答案。 事实是,这个问题与另一个问题重复,我给出了以下答案:
无意中使用=而不是==
所以,我会在这里无耻地引用自己来添加一个重要的信息: if
不是关于比较。 这是关于评估。
这种差异非常重要,因为它意味着任何东西都可以在if
的括号内,只要它可以被计算为布尔值。 这是件好事。
现在,通过禁止=
限制语言,其中所有其他运算符都被授权,对于该语言来说是一个危险的例外,这个例子的使用远非确定,其缺点确实很多。
对于那些对=
typo不感兴趣的人,那么有解决方案(参见下面的替代方案 ……)。
关于if(i = 0)的有效用法[引自我自己]
问题是你正在把问题颠倒过来。 “if”表示法不是像在某些其他语言中那样比较两个值。
C / C ++ if
指令等待任何将计算为boolean或null / non-null值的表达式。 该表达式可以包括两个值比较,和/或可以更复杂。
例如,您可以:
if(i >> 3) { std::cout << "i is less than 8" << std::endl }
这certificate,在C / C ++中,if表达式不限于==和=。 任何事都可以,只要它可以被评估为真或假(C ++),或零非零(C / C ++)。
关于有效用途
回到未引用的答案。
以下表示法:
if(MyObject * p = findMyObject()) { // uses p }
使用户能够声明然后在if中使用p。 这是一个语法糖...但有趣的一个。 例如,想象一下类似XML DOM的对象的情况,其类型在运行时之前是未知的,并且您需要使用RTTI:
void foo(Node * p_p) { if(BodyNode * p = dynamic_cast(p_p)) { // this is a node } else if(SpanNode * p = dynamic_cast(p_p)) { // this is a node } else if(DivNode * p = dynamic_cast(p_p)) { // this is a node } // etc. }
当然,RTTI不应该被滥用,但这只是这种语法糖的一个例子。
另一种用途是使用所谓的C ++ Variable Injection。 在Java中,有这个很酷的关键字:
synchronized(p) { // Now, the Java code is synchronized using p as a mutex }
在C ++中,您也可以这样做。 我没有确切的代码(也没有我发现它的确切的DDJ文章),但这个简单的定义应该足以用于演示目的:
#define synchronized(lock) \ if (auto_lock lock_##__LINE__(lock)) synchronized(p) { // Now, the C++ code is synchronized using p as a mutex }
(请注意,此宏非常原始,不应在生产代码中使用。真正的宏使用if
和a for
。请参阅下面的源代码以获得更正确的实现)。
这是相同的方式,混合注入if
和for
声明,你可以声明一个原始的foreach宏(如果你想要一个工业级的foreach,使用boost)。
关于你的错字问题
您的问题是一个错字,并且有多种方法可以限制代码中的频率。 最重要的是确保左侧操作数是常量。
例如,由于多种原因,此代码将无法编译:
if( NULL = b ) // won't compile because it is illegal // to assign a value to r-values.
甚至更好:
const T a ; // etc. if( a = b ) // Won't compile because it is illegal // to modify a constant object
这就是为什么在我的代码中, const
是你会发现的最常用的关键字之一。 除非我真的想要修改变量,否则它被声明为const
,因此,编译器可以保护我免受大多数错误的影响,包括促使您编写此问题的拼写错误。
但有没有一种情况应该忽略警告,因为这是使用这个“function”的好方法? 我没有看到任何代码清晰度可能的原因,那么它有用吗?
结论
如上例所示,您在问题中使用的function有多种有效用途。
自从我使用此function启用的代码注入后,我自己的代码更清晰,更清晰:
void foo() { // some code LOCK(mutex) { // some code protected by a mutex } FOREACH(char c, MyVectorOfChar) { // using c } }
...这让我很难遇到这个错字的代价是一个可以忽略不计的代价(我不记得上次我写这种类型而没有被编译器抓住)。
有趣的消息来源
我终于找到了关于变量注入的文章。 开始了 !!!
- http://www.drdobbs.com/184401723?pgno=6
- http://www.drdobbs.com/184401728
- http://www.drdobbs.com/184401752
备择方案
如果一个人害怕成为=
/ ==
拼写错误的受害者,那么使用宏可能会有所帮助:
#define EQUALS == #define ARE_EQUALS(lhs,rhs) (lhs == rhs) int main(int argc, char* argv[]) { int a = 25 ; double b = 25 ; if(a EQUALS b) std::cout << "equals" << std::endl ; else std::cout << "NOT equals" << std::endl ; if(ARE_EQUALS(a, b)) std::cout << "equals" << std::endl ; else std::cout << "NOT equals" << std::endl ; return 0 ; }
通过这种方式,可以保护自己免受拼写错误的影响,而不需要语言限制(这会使语言瘫痪),因为很少发生的错误(即几乎从来没有,就我在代码中记得的那样)
决不!
引用的例外情况不会产生编译器警告。 在编译器生成警告的情况下,这绝不是一个好主意。
还有一个方面没有提到:C不会阻止你做任何不必要的事情。 它并不妨碍你这样做,因为C的工作就是给你足够的绳索来吊死自己。 不要以为它比你聪明。 它很擅长。
RegEx样本
RegEx r; if(((r = new RegEx(“\ w *))。IsMatch()){ // ...在这里做点什么 } else if((r = new RegEx(“\ d *”))。IsMatch()){ // ...在这里做点什么 }
作为价值测试
int i = 0; if((i = 1)== 1){ // 1等于分配给int值1的i } 其他{ //? }
我最喜欢的是:
if (CComQIPtr a = BaseClassPtr) { ... } else if (CComQIPtr b = BaseClassPtr) { ... }