В каком случае, если (a = b) хорошая идея? [Дубликат]

== сравнивает ссылки на объекты в Java и не является исключением для объектов String.

Для сравнения фактического содержимого объектов (в том числе String) необходимо использовать equals.

Если сравнение двух объектов String с использованием == оказывается true, это связано с тем, что объекты String были интернированы, а виртуальная машина Java имеет несколько ссылки указывают на тот же экземпляр String. Не следует ожидать сравнения одного объекта String, содержащего то же содержимое, что и другой объект String, используя == для оценки как true.

11
задан Community 23 May 2017 в 12:16
поделиться

17 ответов

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

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

    Оператор = (если он не переопределен) обычно возвращает присвоенное значение. Это позволяет использовать такие операторы, как a = b = c = 3 . В контексте вашего вопроса он также позволяет вам делать что-то вроде этого:

     bool global; // глобальная переменная
    
    //функция
    int foo (bool x) {
    
     // присвоить значение x глобальному
     // если x равно true, вернем 4
    если (глобальный = х)
    возврат 4;
    
     // иначе вернем 3
    возврат 3;
    }
    

    ... что эквивалентно, но короче, чем:

     bool global; // глобальная переменная
    
    //функция
    int foo (bool x) {
    
     // присвоить значение x глобальному
    global = x;
    
     // если x равно true, вернем 4
    если (глобальный == истина)
    возврат 4;
    
     // иначе вернем 3
    возврат 3;
    }
    

    Также следует отметить (как указано Билли ONeal в комментарии ниже), что это также может работать, когда левый аргумент оператора = на самом деле является классом с оператор преобразования , указанный для типа, который может быть приведен (неявно преобразован) к логическому типу. Другими словами, (a = b) будет преобразовано в true или false , если a имеет тип, который может быть приведен к логическому значению.

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

     #include 
    используя пространство имен std;
    class Foo {
    общественность:
    оператор bool () {вернуть истину; }
    Foo () {}
    };
    int main () {
    Foo a;
    Foo b;
    
    если (a = b)
    cout << "истина";
    еще
    cout << "ложь";
    }
    
    // вывод: true
    

    Примечание: На момент написания вышеупомянутое форматирование кода содержит ошибки. Мой код (проверьте исходный код) на самом деле имеет правильный отступ, операторы сдвига и межстрочный интервал. Предполагается, что символы < должны быть <, и не должно быть огромных промежутков между каждой строкой.

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

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

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

     if (a = b) {
     // выполняем какой-то код
    }
    

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

17
ответ дан 3 December 2019 в 00:48
поделиться
while( (l = getline()) != EOF){
        printf("%s\n", l);
}

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

1
ответ дан 3 December 2019 в 00:48
поделиться

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

if ((fp = fopen("filename.txt", "wt")) != NULL) {
    // do something with fp
}
2
ответ дан 3 December 2019 в 00:48
поделиться
void some( int b ) {
    int a = 0;
    if(  a = b ) {
       // or do something with a
       // knowing that is not 0
    } 
    // b remains the same 
 }
2
ответ дан 3 December 2019 в 00:48
поделиться

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

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

Не оставляйте часть «! = 0» вне поля зрения.

Человек, который смотрит на код через 6 месяцев, 1 год, 5 лет, на первый взгляд, просто поверит, что код содержит «классическую ошибку», написанную младшим. программист и постараюсь это «исправить». Вышеупомянутая конструкция ЯСНО указывает на ваше намерение и будет оптимизирована компилятором. Это было бы особенно неудобно, если бы ВЫ были этим человеком.

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

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

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

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

3
ответ дан 3 December 2019 в 00:48
поделиться
while ( (line = readNextLine()) != EOF) {
    processLine();
}
11
ответ дан 3 December 2019 в 00:48
поделиться

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

do {
 ...
} while (current = current->next);
2
ответ дан 3 December 2019 в 00:48
поделиться

Пример RegEx

RegEx r;

if(((r = new RegEx("\w*)).IsMatch()) {
   // ... do something here
}
else if((r = new RegEx("\d*")).IsMatch()) {
   // ... do something here
}

присваивает значение test

int i = 0;
if((i = 1) == 1) {
   // 1 is equal to i that was assigned to a int value 1
}
else {
   // ?
}
0
ответ дан 3 December 2019 в 00:48
поделиться

My fav is:

if (CComQIPtr<DerivedClassA> a = BaseClassPtr)
{
...
}
else if (CComQIPtr<DerivedClassB> b = BaseClassPtr)
{
...
}
0
ответ дан 3 December 2019 в 00:48
поделиться

Никогда!

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

0
ответ дан 3 December 2019 в 00:48
поделиться

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

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

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

1
ответ дан 3 December 2019 в 00:48
поделиться

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

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

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

Просто мои два цента ...

2
ответ дан 3 December 2019 в 00:48
поделиться

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

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

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

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

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

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

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

6
ответ дан 3 December 2019 в 00:48
поделиться

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

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

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

8
ответ дан 3 December 2019 в 00:48
поделиться

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

0
ответ дан 3 December 2019 в 00:48
поделиться

На самом деле это не намеренная особенность C, а следствие двух другие возможности:

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

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

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

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

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

2
ответ дан 3 December 2019 в 00:48
поделиться

Преамбула

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

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

Неумышленное использование = вместо ==

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

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

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

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

О допустимом использовании if(i = 0) [Цитирую от себя]

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

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

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

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

Что доказывает, что в C/C++ выражение if не ограничивается == и =. Подойдет все, что угодно, если только оно может быть оценено как true или false (C++), или zero non-zero (C/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, конечно, не следует злоупотреблять, но это лишь один из примеров использования этого синтаксического сахара.

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

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

В C++ это тоже можно сделать. У меня нет точного кода в голове (как и точной статьи DDJ's, где я обнаружил это), но этого простого define должно быть достаточно для демонстрационных целей:

#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's).

О проблеме опечаток

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

Например, этот код не будет компилироваться по нескольким причинам:

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 ;
}

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

.
1
ответ дан 3 December 2019 в 00:48
поделиться