2013-07-16 6 views
24

Недавно я только что потерял какое-то время, выясняя ошибка в моем коде, который был вызван опечатка:Присваивание в «если» условие

if(a=b) 

вместо:

if(a==b) 

Я задавался вопросом если есть какой-либо конкретный случай, вы хотели бы присвоить значение переменной в операторе if, или если нет, то почему компилятор не выдает предупреждение или ошибку?

+4

В зависимости от того, мой компилятор выдает предупреждение, когда a = b –

+2

Компилятор не выпускает диагностику, потому что это не обязательно. Это вполне допустимый код на C++. –

+0

Следует отметить, что с предупреждениями вы можете, как правило, понять это, компилируя время. – Rapptz

ответ

31
if (Derived* derived = dynamic_cast<Derived*>(base)) { 
    // do stuff with `derived` 
} 

Хотя это часто упоминаются как анти-паттерн («использовать виртуальную отправку!»), Иногда Derived типа имеет функциональные возможности, что Base просто не (и, следовательно, различные функции), и это является хороший способ включить эту семантическую разницу.

+7

Анти-шаблон ставит определение в состояние. Это верный рецепт для нечитабельного и ненадежного кода. –

+31

Включение определения в условие подтягивает область действия, чтобы вы случайно не ссылались на 'производные' на следующей строке после оператора if. По той же причине мы используем 'for (int i = 0 ...' вместо 'int i; for (i = 0; ...'. –

+32

@JamesKanze - Другие считают, что это повышает читаемость и ремонтопригодность, частично путем минимизации область видимости переменной ". Чтобы избежать случайного неправильного использования переменной, обычно рекомендуется ввести переменную в наименьшую возможную область. В частности, обычно лучше отложить определение переменной до тех пор, пока не получится начальное значение ... Одно из самых элегантных приложений этих двух принципов - объявить переменную в условном выражении ». - Stroustrup,« Язык программирования C++ ». –

11

Оператор присваивания возвращает значение присвоенное значение. Таким образом, я мог бы использовать его в подобной ситуации:

if(x = getMyNumber())

Тогда я задаю x быть значение, возвращенное и проверить, если это не ноль.

Избегайте этого, я привел вам пример, чтобы помочь вам понять это.

Редактировать: Добавление Просто предложение (может показаться).

К избежать таких ошибок вверх некоторые проходит один должно написать, если условие, как if(NULL == ptr) вместо if(ptr == NULL) Потому что когда вы орфографических ошибок проверки равенства оператора == как оператор =, компиляция будет бросать Lvalue ошибку с if(NULL = ptr), но if(res = NULL) переданный компилятором (что не то, что вы имеете в виду) и остается ошибкой в ​​коде для времени выполнения.
По той же причине я предпочитаю писать if(getMyNumber() ==x) вместо if(x == getMyNumber())

следует также прочитать: Criticism относительно такого кода.

+9

Я хочу узнать/улучшить себя, пожалуйста, укажите, почему вы делаете снимок. – Maroun

+2

+1 за хороший совет в вашем редактировании! – Hiura

+2

Я часто использую 'if (fp = fopen (" file "," w ")) {// файл не открыт} else {// обработка файлов}' –

1

Выполнение задания в if является довольно распространенным явлением, хотя также часто случается, что люди делают это случайно.

Обычная картина:

if (int x = expensive_function_call()) 
{ 
    // ...do things with x 
} 

Анти-шаблон, где вы ошибочно приписывая вещи:

if (x = 1) 
{ 
    // Always true 
} 
else 
{ 
    // Never happens 
} 

Вы можете избежать этого, до некоторой степени, разместив константы или const , поэтому ваш компилятор выдаст ошибку:

if (1 = x) 
{ 
    // Compiler error, can't assign to 1 
} 

= vs. == - это то, что вам нужно для развития глаз. Я обычно помещаю пробел вокруг оператора, поэтому более очевидно, какая операция выполняется, так как longname=longername очень похож на longname==longername, но = и == сами по себе, очевидно, разные.

+9

Они оба анти-шаблоны. А что касается 'if (1 == x)', это не естественный способ выражения теста, и любой компилятор, заслуживающий его соли, будет предупреждать о 'if (x = 1)', поэтому нет причин для уродства здесь или. –

+0

Шаблон '1 == x', анти-или нет, исходит из [Code Complete] (http://en.wikipedia.org/wiki/Code_Complete), поэтому вы увидите его довольно часто. Это немного уродливо, но может быть разумным требованием для людей, у которых есть проблема с этим правом, выступая в качестве обучающих колес. – tadman

+2

@tadman. Способ научить людей - это раздражать их предупреждениями об этой опечатке, а не переключиться на условия йоды. – stefan

9

Это зависит от того, хотите ли вы написать чистый код или нет.Когда вначале разрабатывался C, важность чистого кода не была полностью распознана, а компиляторы были очень упрощенными: с использованием вложенного назначения, как это часто может привести к ускорению кода . Сегодня я не могу придумать ни одного случая, когда бы это сделал хороший программист . Это просто делает код менее читаемым и более сложно поддерживать.

+1

Я согласен с тем, что это менее читаемо, но только в том случае, если назначение является единственным в выражении if. Если это что-то вроде 'if ((x = function())> 0), то я думаю, что это прекрасно. – ashishduh

+1

@ user1199931 Не совсем. Если вы сканируете код, чтобы получить общее представление об этом, вы увидите 'if' и продолжите, и пропустите тот факт, что есть изменение состояния. Существуют исключения ('for', но само ключевое слово говорит, что оно является циклом и управляет состоянием цикла), но в целом строка кода должна делать одно и только одно. Если он управляет потоком, он не должен изменять состояние. –

+0

«Чистый код» - очень субъективный термин, к сожалению. –

1

why doesn't the compiler throw a warning

Некоторые компиляторы будут генерировать предупреждения о подозрительных присвоений в условном выражении, хотя вы обычно должны включить предупреждение в явном виде.

Например, в Visual C++ вы должны включить C4706 (или предупреждения уровня 4 в целом). Обычно я включаю столько предупреждений, сколько могу, и делаю код более явным, чтобы избежать ложных срабатываний. Например, если бы я действительно хотел сделать это:

if (x = Foo()) { ... } 

Тогда я бы написать:

if ((x = Foo()) != 0) { ... } 

Компилятор видит явное испытание и предполагает, что назначение было сделано намеренно, так что вы не Здесь нет ложного положительного предупреждения.

Единственным недостатком такого подхода является то, что вы не можете использовать его, когда переменная объявлена ​​в условии. То есть, вы не можете переписать:

if (int x = Foo()) { ... } 

в

if ((int x = Foo()) != 0) { ... } 

Синтаксически, что не работает. Таким образом, вы либо должны отключить предупреждение, либо поставить компромисс в отношении того, насколько сильно вы занимаетесь областью x.

+0

Явное сравнение 'if ((x = Foo())! = 0)' не нужно: добавление дополнительной пары круглых скобок 'if ((x = Foo()))' также закроет предупреждение. Кроме того, существует большая разница между 'if (x = Foo())' и 'if (int x = Foo())', позже понятно, что вы хотите сделать инициализацию, а не сравнение, поэтому никакой компилятор в здравом уме, хотел бы предупредить об этом. – cmaster

+2

Чтобы быть справедливым, вам не нужно компрометировать, насколько сильно вы занимаете область 'x', если вы готовы окружить свой блок дополнительными фигурными скобками. Но я считаю, что это не стоит того. –

+0

@cmaster: Явное сравнение необходимо, если вы хотите проверить что-то, кроме '! = 0'. Явное сравнение также яснее, чем дополнительный набор круглых скобок. –

13

Ниже приведена некоторая история этого синтаксиса.

В классическом C, обработка ошибок часто делается, написав что-то вроде:

int error; 
... 
if(error = foo()) { 
    printf("An error occured: %s\nBailing out.\n", strerror(error)); 
    abort(); 
} 

Или, когда там было вызов функции, которая может возвращать пустой указатель, идиома была использована наоборот:

Bar* myBar; 
... //in old C variables had to be declared at the start of the scope 
if(myBar = getBar()) { 
    //do something with myBar 
} 

Однако этот синтаксис опасно близко к

if(myValue == bar()) ... 

, поэтому многие люди рассматривают назначение в состоянии плохого стиля, и компиляторы начали предупреждать об этом (по крайней мере, с -Wall).Тем не менее, это предупреждение можно избежать, добавив дополнительный набор скобок:

if((myBar = getBar())) { //tells the compiler: Yes, I really want to do that assignment! 

Тогда C99 пришли вокруг, что позволяет смешивать определения и утверждения, поэтому многие разработчики часто пишут что-то вроде

Bar* myBar = getBar(); 
if(myBar) { 

который чувствует себя неловко. Поэтому новый стандарт позволяет определения внутри условий, чтобы обеспечить короткий, изящный способ сделать это:

if(Bar* myBar = getBar()) { 

Там нет никакой опасности в этом заявлении больше, вы явно даете переменному тип, явно желая его инициализироваться. Он также избегает дополнительной строки для определения переменной, что приятно. Но самое главное, что компилятор может теперь легко поймать такую ​​ошибку:

if(Bar* myBar = getBar()) { 
    ... 
} 
foo(myBar->baz); //compiler error 
//or, for the C++ enthusiasts: 
myBar->foo();  //compiler error 

Без определения переменной внутри if заявления, это условие не будет обнаружено.

Чтобы сделать длинный ответ коротким: Синтаксис в вашем вопросе является продуктом простоты и силы старого C, но он злой, поэтому компиляторы могут предупредить об этом. Поскольку это также очень полезный способ выразить общую проблему, теперь существует очень краткий, надежный способ добиться такого же поведения. И для этого есть много хороших, возможных применений.

4

Я столкнулся с ситуацией, когда это было полезно совсем недавно, поэтому я решил опубликовать его.

Предположим, вы хотите проверить несколько условий в одном, если, и если какое-либо из условий истинно, вы хотите сгенерировать сообщение об ошибке. Если вы хотите включить в сообщение об ошибке, которое определенное условие вызвало ошибку, вы можете сделать следующее:

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 
} 

Так что, если второе условие является одним, что истинно, е будет равна «кд» , Это связано с поведением короткого замыкания ||, которое предусмотрено стандартом (если только не перегружено). См. this ответ для получения более подробной информации о коротком замыкании.

Смежные вопросы