Есть ли в C ++ не короткозамкнутые логические «и»? - PullRequest
27 голосов
/ 18 ноября 2009

tl; dr: Есть ли в C ++ логическое AND без короткого замыкания (аналогично &&)?

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

IE:

bool Func1(int x, int y){
  if( x > y){
    cout << "ERROR- X > Y" << endl;
  }
}
bool Func2(int z, int q){
  if( q * 3 < z){
    cout << "ERROR- Q < Z/3" << endl;
  }
}
bool Func3(int x, int y, int z, int q){
  return ( Func1(x, y) && Func2(z, q) );
}

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


Сводка ответов:

«Побитовые» операторы | и & может использоваться для получения эффекта, но только если тип возвращаемого значения - bool. Я не нашел упоминания об этом в спецификации ANSI C ++. Из того, что я могу сказать, это работает, потому что "bool" конвертируется в int (true = 1, false = 0), а затем используется побитовый оператор, затем он конвертируется обратно в bool.

Операторы "+" и "*" также могут быть использованы. Это не упоминается в спецификации ANSI C ++, но, вероятно, работает по той же причине, что и выше. "+" give "или" потому что true преобразуется в 1, а затем все, кроме 0, преобразуется обратно в true. «*» работает для «и», потому что 1 (true) * 0 (false) == 0 (false) и 1 (true) * 1 (true) == 1 (true)

Кажется, что оба они полагаются на неявное преобразование типа в целое, а затем обратно в bool. Оба из них, вероятно, испортят любого, кто пытается сохранить код.

Другие ответы сводятся к "Просто используйте временные" или "Реализуйте свои собственные", который не был вопросом. Цель состояла в том, чтобы увидеть, существует ли уже оператор, реализованный в стандарте C ++ для этого.

Ответы [ 7 ]

32 голосов
/ 18 ноября 2009

Оператор & выполняет логические операции "и" для операндов bool и не имеет короткого замыкания.

Это не точка последовательности. Вы не можете полагаться на порядок оценки операндов. Однако гарантируется, что оба операнда вычисляются.

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

21 голосов
/ 18 ноября 2009

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

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

if ( obj != NULL && obj->foo == blah ) { /* do stuff */ }
12 голосов
/ 18 ноября 2009

Да, для этого есть встроенные операторы. + выполняет ИЛИ без короткого замыкания, а * делает AND.

#include <iostream>
using namespace std;

void print(bool b)
{
    cout << boolalpha << b << endl;
}

int main() 
{
    print(true + false);
    print(true * false);
}

Выход:

правда

ложь

4 голосов
/ 18 ноября 2009

Вы можете написать тривиально.

bool LongCircuitAnd( bool term1, bool term2 ) { return term1 && term2; }

bool Func3(int x, int y, int z, int q){
  return LongCircuitAnd( Func1(x,y), Func2(z,q) ); 

И если вы хотите быть очень модным, вы можете даже встроить его !!!

Хорошо, хорошо, если вы действительно не хотите ужасных накладных расходов при вызове функции.

bool Func3(int x, int y, int z, int q){
  return ((int)Func1(x,y)) * ((int)Func2(z,q)); 

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

3 голосов
/ 18 ноября 2009

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

return (b1=Func1()), (b2=Func2()), (b1&&b2);

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

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

class mybool { 
    bool value;
public:
    bool operator&&(mybool const &other) const { 
        return value && other.value;
    }
};

mybool Func1(int, int);
mybool Func2(int, int);

bool Func3(int x, int y, int z, int q) { 
    return Func1(x, y) && Func2(z,q);
}

Хотя это работает, мне кажется, что это слишком «умно» - что-то, что вообще не будет очевидно для большинства читателей. Может помочь и другое название mybool, но я не могу придумать, какое имя очень хорошо отражает намерение, не становясь настолько многословным, что это будет чистый убыток.

2 голосов
/ 07 апреля 2011

Почти универсальный, но часто недокументированный нестандартный оператор был введен именно для этой цели, впервые разработанным GCC наряду с x ?: y (x, если не ноль, иначе y), и теперь, к сожалению, удален * Операторы 1004 * и <? мин / макс и их составные формы назначения (см. http://gcc.gnu.org/onlinedocs/gcc/Deprecated-Features.html). К сожалению, с уже используемыми & и && они, похоже, соскребли дно ствола, чтобы найти подходящая последовательность символов, но это только мое мнение - приветствуются любые исторические объяснения того, почему это могло быть выбрано.

Итак, хотя в настоящее время он не так хорошо известен, как многие другие операторы, оператор >! (правильно, но скучно называть его «длинным контуром и», но в разговорной речи «больший узел», чем у знать) был добавлен большинством компиляторов C и C ++ (включая GCC и даже MSVC ++) для удовлетворения этого требования:

bool f1() { ... }
bool f2() { ... }

...
bool f3() { return f1() >! f2(); }

Возьми это за спин; -).

2 голосов
/ 18 ноября 2009

Да. Перегруженные версии operator&& и operator|| не закорачивают - они оценивают оба операнда, даже если левый операнд «определяет» результат ... ( Источник )

Как говорится, не перегружайте operator&& или operator||. Будьте добры к вашим программистам, которые будут смотреть на && или || и предполагать, что они делают короткое замыкание.

...