C ++编译器通过您编写的警告告诉您
if( a = b ) { //...
并且这可能是一个错误,你当然想写
if( a == b ) { //...
但是有一种情况应该忽略警告,因为这是使用这个“功能”的好方法吗? 我没有看到任何代码清晰度可能的原因,那么它是否有用?
答案 0 :(得分:18)
两个可能的原因:
分配&检查强>
=
运算符(未覆盖时)通常会返回它指定的值。这是为了允许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在下面的评论中所述),当=
运算符的左侧参数实际上是具有conversion operator的类时,这也可以起作用为可以强制(隐式转换)为bool的类型指定。换句话说,如果某个类型可以强制转换为布尔值,则(a=b)
将转换为true
或false
。
所以以下是与上面类似的情况,除了=
的左手参数是一个对象而不是一个bool:
#include <iostream>
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
注意:在撰写本文时,上面的代码格式会被窃听。我的代码(检查源代码)实际上具有适当的缩进,移位运算符和行间距。 <
应该是&lt;'s s,并且每行之间不应该有间隙。
重写=
运算符
由于C ++允许覆盖运算符,因此有时候=将覆盖除了原始类型之外的其他内容。在这些情况下,对对象执行=
操作可能会返回一个布尔值(如果为该对象类型覆盖=
运算符的话)。
因此,以下代码将以=
为a
作为参数执行b
操作。然后它会有条件地执行一些代码,具体取决于该操作的返回值:
if (a=b){
//execute some code
}
在这里,a
必须是一个对象,b
的类型正确,如=
运算符覆盖a
'对象所定义的那样s型。要了解有关运算符覆盖的更多信息,请参阅此维基百科文章,其中包含C ++示例:Wikipedia article on operator overriding
答案 1 :(得分:12)
while ( (line = readNextLine()) != EOF) {
processLine();
}
答案 2 :(得分:8)
您可以用来测试函数是否返回任何错误
if (error_no = some_function(...)) {
//handle error
}
假设some_function在出现错误时返回错误代码,否则返回零
答案 3 :(得分:6)
这是C语言基本功能的结果:
赋值操作的值是指定值本身。
您可以将“返回值”用作if()
语句的条件这一事实是偶然的。
顺便说一下,这就是允许这种疯狂简洁的伎俩:
void strcpy(char *s, char *t)
{
while( *s++ = *t++ );
}
当然,当达到t
中的nullchar时,while会退出,但同时会将其复制到目标s
字符串。
这是否是一个好主意,通常不是,因为它会降低代码的可读性并且容易出错。
答案 4 :(得分:3)
虽然构造是完全合法的语法,你的意图可能真的如下所示:
if( (a = b) != 0 ) {
...
}
不要将“!= 0”部分留下。
乍一看6个月,1年,5年后看代码的人,只是相信代码中包含一个由jr写的“经典bug”。程序员并将尝试“修复”它。 CLEARLY上面的结构表明了你的意图,并将由编译器进行优化。如果你是那个人,这将特别令人尴尬。
您的另一个选择是重复评论。但以上是自我记录的代码,这是更好的。
最后,我的偏好是这样做:
a = b;
if( a != 0 ) {
...
}
这很清楚,因为代码可以获得。如果性能受到影响,则几乎为零。
答案 5 :(得分:2)
void some( int b ) {
int a = 0;
if( a = b ) {
// or do something with a
// knowing that is not 0
}
// b remains the same
}
答案 6 :(得分:2)
直接回答这个问题,作为个人意见,我真的不认为这是一个好主意。
我的意思是,我知道使用这种语法可以避免在代码中添加额外的行,但我认为它会从代码中消除一些可读性。
这种语法对于像@Steven Schlansker建议的那样非常有用,但直接使用它作为一个条件不是一个好主意。
只是我的两分钱......
答案 7 :(得分:2)
您应该以更好的编码方式明确地编写检查语句,避免 assign&amp;检查方法。例如:
if ((fp = fopen("filename.txt", "wt")) != NULL) {
// do something with fp
}
答案 8 :(得分:2)
一个有用的常见例子可能是:
do {
...
} while (current = current->next);
答案 9 :(得分:2)
这实际上不是C的故意特征,而是其他两个特征的结果:
作业返回指定值
这对于执行a = b = 0
等多个分配或while ((n = getchar()) != EOF)
等循环非常有用。
数字和指针具有真值
C在1999年标准之前最初没有bool
类型,所以它使用int
来表示布尔值。向后兼容性要求C和C ++允许bool
,if
和while
中的非for
表达式。
因此,如果a = b
有值且if
对它接受的值是否宽松,则if (a = b)
有效。但我建议使用if ((a = b) != 0)
来阻止任何人“修理”它。
答案 10 :(得分:1)
while( (l = getline()) != EOF){
printf("%s\n", l);
}
这当然是最简单的例子,很多时候这很有用。要记住的主要事情是(a = true)返回true,就像(a = false)返回false一样。
答案 11 :(得分:1)
请注意,这个答案是关于C ++的(我在添加标签“C”之前开始写这个答案)。
在阅读Jens Gustedt的评论之后,我意识到这不是我第一次写这样的答案。说实话,这个问题与另一个问题重复,我给出了以下答案:
Inadvertent use of = instead of ==
所以,我会在这里无耻地引用自己来添加重要信息: if
不是关于比较。这是关于评估。
这种差异非常重要,因为它意味着任何东西都可以在if
的括号内,只要它可以被计算为布尔值。 这是件好事。
现在,通过禁止=
限制语言,其中所有其他运算符都被授权,对于该语言来说是一个危险的例外,这个例子的使用远非确定,其缺点确实很多。 / p>
对于那些对=
错字不感兴趣的人,有解决方案(参见下面的替代)。
问题在于你正在解决这个问题。 “if”表示法不是与其他语言中的两个值进行比较。
C / C ++ if
指令等待任何将计算为布尔值或null /非null值的表达式。该表达式可以包括两个值比较,和/或可以更复杂。
例如,您可以:
if(i >> 3)
{
std::cout << "i is less than 8" << std::endl
}
这证明,在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<BodyNode *>(p_p))
{
// this is a <body> node
}
else if(SpanNode * p = dynamic_cast<SpanNode *>(p_p))
{
// this is a <span> node
}
else if(DivNode * p = dynamic_cast<DivNode *>(p_p))
{
// this is a <div> 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
和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
,因此,编译器可以保护我免受大多数错误的影响,包括促使您编写此问题的拼写错误。
但是有一种情况应该忽略警告,因为这是使用这个“功能”的好方法吗?我没有看到任何代码清晰度可能的原因,那么它是否有用?
如上例所示,您在问题中使用的功能有多种有效用途。
由于我使用了此功能启用的代码注入,因此我自己的代码更清晰,更清晰:
void foo()
{
// some code
LOCK(mutex)
{
// some code protected by a mutex
}
FOREACH(char c, MyVectorOfChar)
{
// using c
}
}
...这让我难以接受这个错字的代价是一个可以忽略不计的代价(而且我记不起上次写这种类型而没有被编译器抓住)。
我终于找到了关于变量注射的文章。我们走了!!!
如果有人担心会成为=
/ ==
错字的受害者,那么使用宏可能会有所帮助:
#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 ;
}
通过这种方式,人们可以保护自己免受拼写错误的影响,而不需要语言限制(这会使语言瘫痪),因为很少发生的错误(即几乎从未在我的代码中记住它)
答案 12 :(得分:1)
但是有警告的情况 应该被忽略,因为它是好的 如何使用这个“功能”?我没有看到 任何代码清晰度原因都是可能的 有用的情况吗?
可以通过在作业周围添加额外的括号来抑制警告。这样澄清了程序员的意图。我见过的与(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, e.g.)
}
else if ( (a = expression2_with_zero_for_failure) )
{
// do something with 'a' to avoid having to reevaluate
// 'expression2_with_zero_for_failure'
}
// etc.
至于编写这种代码是否足够有用,可以证明使用C ++时初学者(有时甚至是最糟糕时刻的专业人士)遇到的常见错误,很难说。这是继承自C和Stroustrup的遗产,而其他有助于设计C ++的遗产可能已经变得完全不同,更安全,因为他们没有尽可能地使C ++向后兼容C语言。
我个人认为这不值得。我在一个团队工作,之前我曾多次遇到过这个bug。我本来会赞成不允许它(至少需要括号或其他一些明确的语法,否则它被认为是构建错误)以换取减轻遇到这些错误的负担。
答案 13 :(得分:0)
<强>从不!强>
引用的例外情况不会产生编译器警告。在编译器生成警告的情况下,这绝不是一个好主意。
答案 14 :(得分:0)
有一个方面尚未提及:C不会阻止你做任何不必要的事情。它并不妨碍你这样做,因为C的工作就是给你足够的绳索来吊死自己。不要以为它比你聪明。它很擅长。
答案 15 :(得分:0)
RegEx样本
RegEx r; if(((r = new RegEx("\w*)).IsMatch()) { // ... do something here } else if((r = new RegEx("\d*")).IsMatch()) { // ... do something here }
指定一个值测试
int i = 0; if((i = 1) == 1) { // 1 is equal to i that was assigned to a int value 1 } else { // ? }
答案 16 :(得分:0)
我最喜欢的是:
if (CComQIPtr<DerivedClassA> a = BaseClassPtr)
{
...
}
else if (CComQIPtr<DerivedClassB> b = BaseClassPtr)
{
...
}