Мы можем написать оператор if
как
if (a == 5, b == 6, ... , thisMustBeTrue)
и только последнее условие должно быть выполнимым, чтобы войти в тело if
.
почему это разрешено?
Мы можем написать оператор if
как
if (a == 5, b == 6, ... , thisMustBeTrue)
и только последнее условие должно быть выполнимым, чтобы войти в тело if
.
почему это разрешено?
Немного изменив ваш пример, предположим, что это было
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 требует синтаксиса для записи двух (или более) несвязанных выражений в позиции, где обычно вы можете написать только одно выражение.
Теперь условие оператора if
является (среди прочего) таким местом и, следовательно, вы также можете использовать оператор ,
там - имеет ли смысл это делать или нет, это совершенно другой вопрос! В частности - и отличается от, например, вызовы функций или объявления переменных - запятая здесь не имеет особого значения, поэтому она делает то, что она всегда делает: она вычисляет выражения слева и справа, но возвращает результат только одного, который затем используется if
.
Единственные два момента, о которых я могу думать прямо сейчас, когда использование (неперегруженного) ,
-оператора имеет смысл:
Если вы хотите увеличить несколько итераторов в начале цикла for
:
for ( ... ; ... ; ++i1, ++i2){
*i2=*i1;
}
Если вы хотите оценить более одного выражения в функции constexpr С++ 11.
Повторить это еще раз: использование оператора запятой в выражении if
или while
- в том виде, в котором вы его показали в вашем примере, - это не разумно. Это просто еще один пример, когда синтаксисы языка 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. Совокупное выражение - это конструкция, которая понимается из теории (и некоторых других языков) и будет отсутствовать без добавления ее в виде запятой. Его использование в заявлении for
было оригинальным обоснованием того, зачем им это нужно.
Но, сделав язык более полным с теоретической точки зрения, он позже находит использование, которое никто не планировал. Ранний С++ был транслятором, который сгенерировал C как свой вывод, и наличие последовательного выражения было абсолютно необходимым, позволяя встроенным функциям действительно генерировать логику "в строке" в коде C.
Это включает любое место, в котором появляется выражение, включая условие оператора if
.
Аналогично, он использовался в "интересных" макросах. И так же, как С++ покончил с макросами, предоставив встроенные функции, в то время как компиляторы до x11 нашли цикл Boost FOREACH (в конечном счете, эмуляцию функции, добавленной к языку в x11) очень удобно, и это было дьявольски умный набор макросов, в котором задействован оператор запятой.
(Hmm, текущая версия расширяется на несколько операторов, используя цепочку if
/else
, а не перебирая все это в один while
.)
Теперь есть другой способ помещать любое выражение в выражение (lambdas), поэтому будущий сумасшедший бизнес макросов, которые будут эмулировать еще более новые языковые функции или встроенные языки, специфичные для домена, возможно, больше не понадобится использовать.
Итак, не пишите такой код. Если это не ясно и действительно проще, чем писать вспомогательные функции или разбивать на несколько утверждений.
Но это может быть просто макрос, который вы хотите легко использовать в одном месте, и это место находится внутри парнеров if
или while
. Это может быть оправдано в доменном языке, размещенном внутри исходного кода С++, или функция эмуляции языка, например (возможно) альтернатива обработке исключений, используемой во встроенной реальной система времени.
Короче говоря, у него нет нормального хорошего использования. Но это там для полноты, и вы никогда не знаете, когда кто-то найдет это полезным.