Недавно я наткнулся на фрагмент кода, который выглядел так:
bool MyClass::do_work()
{
bool success = true;
for (auto const& worker : m_workers)
{
success &= worker.do_work(); // worker.do_work() returns a bool
}
return success;
}
Если я правильно понимаю, функция возвращает true, если все рабочие возвращают true, и возвращает false, если какой-либо рабочий возвращает ложь. Однако он всегда оценивает всех рабочих (что желательно). Оценка короткого замыкания не выполняется, поскольку использовался побитовый оператор &=, а не логический оператор &&.
Гарантировано ли такое поведение? Точнее, гарантируется ли, что побитовый & всегда оценивает оба операнда, даже если они имеют тип bool? Я встречал много ответов SO относительно гарантированной оценки короткого замыкания для &&, но ни один из них не утверждает, что для & существует гарантированная оценка без короткого замыкания.
Если такое поведение гарантировано, это хороший стиль программирования? Мне потребовалось больше, чем беглый взгляд, чтобы понять функцию, потому что я раньше не видел этого стиля, и сначала я был сбит с толку, была ли задействована оценка короткого замыкания или нет.
Есть ли лучшая альтернатива, чем следующая?
bool MyClass::do_work()
{
bool success = true;
for (auto const& worker : m_workers)
{
if (!worker.do_work())
{
success = false;
}
}
return success;
}
Обычно оцениваются все операнды оператора, за исключением операторов &&, || и ?:. Стандарт даже упоминает для &&, что «В отличие от &, && гарантирует оценку слева направо: второй операнд не оценивается, если первым операндом является false».. Что касается хорошего стиля программирования, то это основано на мнении.
@ Вы, я не думаю, что стандарт гарантирует, что all_of не производит короткого замыкания, это было бы действительно странно.
@Holt Как all_of связан с поразрядным &?
@Holt: Я понял это ок. Через 3 секунды после публикации :)
@pschill Я отвечал на теперь удаленный комментарий от Ты;)
"Есть ли лучшая альтернатива [...]?" Немного возразил бы, да (?): std::count_if (m_workers.begin(), m_workers.end(), [](const auto& worker) { return !worker.do_work(); }) == 0;
Или success = worker.do_work() && success;, если вы хотите придерживаться императивного цикла.





Если это явно не указано в стандарте, все операнды оператора оцениваются и распределяются по порядку. 1 в C++:
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. [...]
Единственные три исключения, которые приходят мне в голову, - это операторы 2&&, || и ?:.
Стандарт даже упоминает для &&3, что:
Unlike
&,&&guarantees left-to-right evaluation: the second operand is not evaluated if the first operand isfalse.
Что касается хорошего стиля программирования, то это основано на мнении.
1 Unsequenced basically means that if you have A @ B (where @ is an operator), B (and its side effects) can be evaluated before A, which is why construct such as i++ + ++i are undefined behavior.
2 Note that for overloaded && and || operator, this is not true anymore since both operands are evaluated. ?: cannot be overloaded.
3 There is a similar note for | within [expr.log.or].
&, && гарантирует оценку слева направо: второй операнд не оценивается, если первым операндом является false.. Позвольте мне перефразировать это: & не гарантирует оценку слева направо. К сожалению, это утверждение означает, что оператор & может выполнить оценку короткого замыкания, это просто не гарантируется.
@pschill Нет, это означает, что & может оценивать правый операнд перед левым, как и большинство операторов. Когда вы выполняете A op B, B можно оценить до A, но будут оцениваться оба, за исключением &&, || и ?:.
@pschill Версия || немного лучше, если вы предпочитаете: «В отличие от |, || гарантирует оценку слева направо; Кроме того, второй операнд не оценивается, если первый операнд оценивается как истина».
@pschill Я отредактировал свой ответ, чтобы добавить стандартную цитату относительно поведения операторов по умолчанию, в которой говорится, что все операнды оператора оцениваются, если не указано иное.
Итак, дело в том, что операторы (кроме && и т. д.) Оцениваются как «обычные» функции C++, что означает, что оцениваются все аргументы. Спасибо :)
Я бы сказал, что это очень запутанный стиль: кажется, что легко упустить из виду эффект отсутствия короткого замыкания.