В чем преимущество запятых в условном выражении?


мы можем написать if заявление

if (a == 5, b == 6, ... , thisMustBeTrue)

и только последнее условие должно быть выполнено, чтобы ввести if тело.

почему это допускается?

7 60

7 ответов:

слегка изменив свой пример, предположим, что это было

if ( a = f(5), b = f(6), ... , thisMustBeTrue(a, b) )

(обратите внимание на = вместо ==). В этом случае запятые гарантируют порядок оценки слева направо. В отличии от этого, с этим

if ( thisMustBeTrue(f(5), f(6)) )

вы не знаете, если f(5) вызывается до или после f(6).

более формально, запятые позволяют писать последовательность выражения (a,b,c) таким же образом вы можете использовать ; для записи последовательности заявления a; b; c;. И просто как ; создает точка последовательности (конец полного выражения) так и запятую. Порядок оценки определяется только точками последовательности, см. этот пост.

но конечно, в этом случае, вы бы на самом деле написать это

a = f(5);
b = f(6);    
if ( thisMustBeTrue(a, b) )

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

короче: Хотя это законно, обычно не имеет смысла использовать оператор запятой в части условия if или while заявление (EDIT: хотя последнее иногда может быть полезно, как объясняет user5534870 в своем ответе).

более подробное объяснение: Помимо его синтаксической функции (например, разделение элементов в списках инициализаторов, объявления переменных или вызовы/объявления функций), в C и c++, элемент , также может быть нормальной оператор так же, как например +, и поэтому его можно использовать везде, где разрешено выражение (В C++ вы можете даже перегрузить его).
Отличие от большинства других операторов заключается в том, что, хотя обе стороны оцениваются, он никак не объединяет выходы левого и правого выражений, а просто возвращает правый.
Это было введено, потому что кто - то (вероятно, Деннис Ричи) решил почему-то, что C требуется синтаксис для записи двух (или более) несвязанных выражений в позиции, где обычно можно написать только одно выражение.

теперь, состояние if оператор (среди прочих) такое место и, следовательно, вы также можете использовать , оператор там - имеет ли смысл делать это или нет-это совершенно другой вопрос! В частности - и в отличие от, например, вызовов функций или объявлений переменных - запятая там не имеет особого значения, поэтому она делает то, что он всегда делает: он оценивает выражения слева и справа, но возвращает только результат правого, который затем используется if заявление.

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

  1. если вы хотите увеличить несколько итераторов в голове for петли:

    for ( ... ; ... ; ++i1, ++i2){
        *i2=*i1;
    }
    
  2. если вы хотите вычислите несколько выражений в функции constexpr C++11.

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

на if утверждение, нет никакого реального смысла помещать что-то в выражение запятой, а не снаружи.

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

так как насчет s do...while заявление? Там нам остается только беспокоиться о самой петле, верно? Оказывается, что даже здесь запятую выражение можно смело заменить, переместив первую часть в цикл.

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

нет преимущество: оператор запятой-это просто выражение с типом последнего выражения в его списке выражений, а оператор if вычисляет логическое выражение.

if(<expr>) { ... }
 with type of <expr> boolean

это странный оператор true, но в нем нет магии - за исключением того, что он путает списки выражений со списками аргументов в вызовах функций.

foo(<args>)
 with <args> := [<expr>[, <expr>]*]

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

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

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

void calculateValue(FooType &result) {/*...*/}

тогда как вы используете условные операторы, которые зависят от result?

вы можете объявить переменная, которая будет изменена, затем проверьте ее с помощью if:

FooType result;
calculateValue(result);
if (result.isBared()) {
    //...
}

это может быть сокращен до

FooType result;
if (calculateValue(result) , result.isBared()) {
    //...
}

которые на самом деле не стоит. на while петли там могут быть некоторые небольшие преимущества. Если calculateValue должен / может быть вызван, пока результат больше не bar ' d, у нас было бы что-то вроде:

FooType result;
calculateValue(result);  //[1] Duplicated code, see [2]
while (result.isBared()) {
    //... possibly many lines
    //separating the two places where result is modified and tested

    //How do you prevent someone coming after you and adds a `continue`
    //here which prevents result to be updated in the and of the loop?

    calculateValue(result); //[2] Duplicated code, see [1]
}

и может быть сокращен до:

FooType result;
while (calculateValue(result) , result.isBared()) {
    //all your (possibly numerous) code lines go here
}

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

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

ErrorType fallibleCalculation(FooType &result) {/*...*/}

затем

FooType result;
ErrorType error;

while (error = fallibleCalculation(result) , (Success==error && result.isBared())) {
    //...
}

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

FooType result;
ErrorType error;

while (Success == fallibleCalculation(result) && result.isBared()) {
    //...
}

ничего не было. Сравнение на a в этом коде совершенно излишним.

мой вопрос в том, что такое преимущество запятых в if или while заявление? Почему это разрешено ?

он существует, потому что операторы и выражения-это разные вещи в C. A compound выражение это конструкция, которая понимается из теории (и некоторых других языков) и будет отсутствовать, не добавив его в виде запятой. Его использование в for заявление было первоначальным обоснованием того, зачем они нужны оно.

выражение было абсолютно необходимо, чтобы встроенные функции действительно генерировали логику "в строке"в коде C.

это включает в себя любое место, где появляется выражение,в том числе состояние if заявление.

аналогично, он был использован в" интересных " макросах. И так же, как C++ покончил с макросами, предоставив встроенные функции, так как компиляторы up-to-x11 нашли цикл Boost FOREACH range (в конечном счете, эмуляция функции, добавленной к языку в x11) очень удобной, и это был чертовски умный набор макросов, который включал оператор запятой.

(Хм, текущая версия расширяется на несколько операторов с помощью chained if/else, вместо того, чтобы запихивать все это в один while.)

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


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

но это может быть только вещь для макроса, который вы хотите использовать легко в одном месте и это место находится внутри скобкой if или while. Это может быть оправдано в проблемно-ориентированный язык размещенный внутри исходного кода C++, или функция эмуляции языка как (возможно) альтернатива обработке исключений, используемой во встроенной системе реального времени.

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