В этом случае, если (a = b) является хорошей идеей?

Возможный дубликат:
Непреднамеренное использование = вместо ==

Компиляторы С++ сообщают вам о предупреждениях, которые вы написали

if( a = b ) { //...

и что это может быть ошибкой, что вы, конечно же, хотели написать

if( a == b ) { //...

Но есть ли случай, когда предупреждение следует игнорировать, потому что это хороший способ использовать эту "функцию"? Я не вижу никакой причины ясности кода, так есть ли случай, когда он полезен?

Ответ 1

Две возможные причины:

  • Назначить и проверить

    Оператор = (если он не переопределяется) обычно возвращает значение, которое он назначил. Это позволяет использовать такие выражения, как 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 в комментарии ниже), что это также может работать, когда левый аргумент оператора = фактически является классом с оператор преобразования, указанный для типа, который может быть принудительно преобразован в bool. Другими словами, (a=b) будет evaulate к true или false, если a имеет тип, который может быть принудительно привязан к логическому значению.

    Итак, следующая ситуация аналогична предыдущей, за исключением того, что левый аргумент = - это объект, а не 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, и не должно быть чрезмерных промежутков между каждой линией.

  • Переопределенный оператор =

    Так как С++ допускает переопределение операторов, иногда = будет переопределяться, чтобы делать что-то другое, чем то, что он делает с примитивными типами. В этих случаях выполнение операции = объекта может возвращать логическое значение (если то, как оператор = был переопределен для этого типа объекта).

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

    if (a=b){
       //execute some code
    }
    

    Здесь a должен быть объектом, а b будет иметь правильный тип, как определено переопределением оператора = для объектов типа a. Чтобы узнать больше о переопределении оператора, см. Эту статью в Википедии, которая включает примеры С++: Статья в Википедии о переопределении оператора

Ответ 2

while ( (line = readNextLine()) != EOF) {
    processLine();
}

Ответ 3

Вы можете использовать, чтобы проверить, вернула ли функция какая-либо ошибка

if (error_no = some_function(...)) {
    //handle error
}

Предполагая, что some_function возвращает код ошибки в случае ошибки или нуля в противном случае

Ответ 4

Это является следствием основной особенности языка C:

Значение операции присваивания - это само назначенное значение.

Тот факт, что вы можете использовать это "возвращаемое значение" как условие оператора if(), является случайным.

Кстати, это тот же трюк, который позволяет эту сумасшедшую лаконичность:

void strcpy(char *s, char *t)
{
    while( *s++ = *t++ );
}

Конечно, while выходит, когда nullchar в t достигнут, но в то же время он копируется в строку назначения s.

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

Ответ 5

Хотя конструкция является совершенно законным синтаксисом, и ВАШЕ намерение действительно может быть показано ниже:

if( (a = b) != 0 ) {
   ...
}

Не оставляйте часть "!= 0".

Человек, смотрящий на код 6 месяцев, 1 год, через 5 лет, на первый взгляд, просто поверит, что код содержит "классическую ошибку", написанную младшим. программист и попытается "исправить" его. Конструкция над CLEARLY указывает ваше намерение и будет оптимизирована компилятором. Это было бы особенно неловко, если бы вы были этим человеком.

Другой вариант - это сильно с комментариями. Но выше это самодокументирующий код, который лучше.

Наконец, я предпочитаю сделать это:

a = b;
if( a != 0 ) {
   ...
}

Это примерно ясно, как может получить код. Если есть удар производительности, он практически равен нулю.

Ответ 6

void some( int b ) {
    int a = 0;
    if(  a = b ) {
       // or do something with a
       // knowing that is not 0
    } 
    // b remains the same 
 }

Ответ 7

Идя прямо к вопросу, и, как личное мнение, я действительно не думаю, что есть случай, когда это хорошая идея.

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

Этот синтаксис очень полезен для таких вещей, как предложенный @Steven Schlansker, но использование его непосредственно в качестве условия не является хорошей идеей.

Только мои два цента...

Ответ 8

На самом деле это не преднамеренная функция C, а следствие двух других функций:

Назначение возвращает назначенное значение

Это полезно для выполнения нескольких назначений, таких как a = b = 0, или циклов типа while ((n = getchar()) != EOF).

Числа и указатели имеют значения истинности

C изначально не имел типа bool до 1999 года, поэтому он использовал int для представления булевых значений. Для обратной совместимости требуется, чтобы C и С++ допускали выражения не bool в if, while и for.

Итак, если a = b имеет значение и if является мягким относительно того, какие значения он принимает, то работает if (a = b). Но я бы рекомендовал вместо этого использовать if ((a = b) != 0), чтобы отговорить кого-либо от "исправления".

Ответ 9

Обычный пример, где это полезно, может быть:

do {
 ...
} while (current = current->next);

Ответ 10

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

if ((fp = fopen("filename.txt", "wt")) != NULL) {
    // do something with fp
}

Ответ 11

while( (l = getline()) != EOF){
        printf("%s\n", l);
}

Это, конечно, самый простой пример, есть много раз, когда это полезно. Самое главное помнить, что (a = true) возвращает true, так же как (a = false) возвращает false.

Ответ 12

Но есть ли случай, когда предупреждение следует игнорировать, потому что это хорошо способ использовать эту "функцию"? Я не вижу любая причина четкости кода, так там, где его полезно?

Предупреждение можно подавить, разместив дополнительные скобки вокруг назначения. Это объясняет намерение программиста. Обычные случаи, которые я видел, которые будут соответствовать (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 и Stroustrup, а другие, вносящие вклад в дизайн С++, могли пойти совершенно другим, более безопасным путем, если бы они не пытались сделать С++ обратно совместимым с C как можно больше.

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

Ответ 13

Преамбула

Обратите внимание, что этот ответ относится к С++ (я начал писать этот ответ до добавления тега "C" ).

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

Непреднамеренное использование = вместо ==

Итак, я бесстыдно цитирую себя здесь, чтобы добавить важную информацию: if не о сравнении. Это об оценке.

Это различие очень важно, потому что оно означает, что в круглых скобках if может быть что угодно, если его можно вычислить как логическое. И это хорошо.

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

Для тех, кто неспособен с опечаткой =, тогда есть решения (см. Альтернативы ниже...).

О допустимых действиях if (i = 0) [Цитируется от меня]

Проблема в том, что вы делаете проблему с ног на голову. Обозначение "if" не означает сравнение двух значений, как на некоторых других языках.

Инструкция C/С++ if ожидает выражения, которые будут вычисляться как с булевым, так и с нулевым/ненулевым значением. Это выражение может включать в себя два сравнения значений и/или может быть намного сложнее.

Например, вы можете:

if(i >> 3)
{
   std::cout << "i is less than 8" << std::endl
}

Что доказывает, что в C/С++ выражение if не ограничено == и =. Все будет сделано, если оно может быть оценено как истинное или ложное (С++), или ноль ненулевой (C/С++).

О допустимых действиях

Возврат к нециклируемому ответу.

Следующие обозначения:

if(MyObject * p = findMyObject())
{
   // uses p
}

позволяет пользователю объявлять, а затем использовать p внутри if. Это синтаксический сахар... Но интересный. Например, представьте случай 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 не следует злоупотреблять, конечно, но это всего лишь один пример этого синтаксического сахара.

Другим вариантом было бы использовать то, что называется С++ Variable Injection. В Java это ключевое слово cool:

synchronized(p)
{
   // Now, the Java code is synchronized using p as a mutex
}

В С++ вы тоже можете это сделать. У меня нет точного кода (ни точной статьи 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. См. sources ниже для более правильная реализация).

Точно так же, смешивая инъекцию с объявлением 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 ;
}

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

Ответ 14

Никогда!

Приведенные исключения не генерируют предупреждения компилятора. В случаях, когда компилятор генерирует предупреждение, это никогда не будет хорошей идеей.

Ответ 15

Есть один аспект этого, о котором не упоминалось: C не мешает вам делать что-либо, что ему не нужно. Это не мешает вам делать это, потому что C-задание - дать вам достаточно веревки, чтобы повесить себя. Не думать, что это умнее тебя. И это хорошо.

Ответ 16

Пример 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 {
   // ?
}

Ответ 17

Мой fav:

if (CComQIPtr<DerivedClassA> a = BaseClassPtr)
{
...
}
else if (CComQIPtr<DerivedClassB> b = BaseClassPtr)
{
...
}