Почему в c ++ нет && = или || = для логических значений?

126

Есть ли «очень плохо» , что может произойти , &&=и ||=были использованы в качестве синтаксического сахара для bool foo = foo && barи bool foo = foo || bar?

каче
источник
5
См. Другой вопрос: stackoverflow.com/questions/2324549/… Этот вопрос касается Java, но при использовании линии C в основном применимы те же аргументы.
jamesdlin
По сути, в C ++ его нет, потому что они его не вставляли - в таких языках, как Ruby, он есть. бу ...
Kache
2
Но в Ruby это x ||= yпримерно не эквивалентно C ++ x = x ? x : y;для любого типа? Другими словами, «установить в y, если еще не установлено». Это значительно более полезно, чем C или C ++ x ||= y, которые (запрещая перегрузку оператора) будут делать «установить x, (bool)yесли он еще не установлен». Я не хочу добавлять для этого еще одного оператора, это кажется немного слабым. Просто напишите if (!x) x = (bool)y. Но тогда я на самом деле не использую boolдостаточно переменных, чтобы требовать дополнительных операторов, которые действительно полезны только с этим одним типом.
Стив Джессоп,
1
Я уверен, что основная причина, по которой в C ++ их нет &&=или ||=просто в том, что в C их нет. Я разумно уверен, что причина их отсутствия в C заключается в том, что функциональность не была сочтена достаточно полезной.
Джонатан Леффлер,
2
Кроме того, будучи сверхпедантичным, нотация bool foo = foo || bar;вызовет неопределенное поведение, поскольку fooне инициализируется до оценки foo || bar. Конечно, это должно быть что-то вроде, bool foo = …initialization…; …; foo = foo || bar;и тогда вопрос остается актуальным.
Джонатан Леффлер

Ответы:

73

А boolможет быть только trueили falseв C ++. Таким образом, использование &=и |=относительно безопасно (хотя мне не особенно нравятся обозначения). Верно, они будут выполнять битовые операции, а не логические (и, следовательно, они не будут сокращать замыкание), но эти битовые операции следуют четко определенному отображению, которое фактически эквивалентно логическим операциям, если оба операнда имеют типbool , 1

Вопреки тому, что здесь говорили другие люди, boolв C ++ никогда не должно быть другого значения, например 2. При присвоении этого значения a boolоно будет преобразовано trueв соответствии со стандартом.

Единственный способ получить недопустимое значение в a bool- использовать reinterpret_castуказатели:

int i = 2;
bool b = *reinterpret_cast<bool*>(&i);
b |= true; // MAY yield 3 (but doesn’t on my PC!)

Но поскольку этот код в любом случае приводит к неопределенному поведению, мы можем спокойно игнорировать эту потенциальную проблему при согласовании кода C ++.


1 По общему признанию, это довольно серьезная оговорка, как показывает комментарий Энгью:

bool b = true;
b &= 2; // yields `false`.

Причина в том, что b & 2целочисленное продвижение выполняется таким образом, что выражение затем эквивалентно выражению static_cast<int>(b) & 2, в результате 0чего получается выражение , которое затем преобразуется обратно в bool. Так что это правда, что существование объекта operator &&=улучшит безопасность типов.

Конрад Рудольф
источник
4
Но && и || операторы будут работать со всем, что преобразуется в bool, а не только с bool.
dan04
13
Они не делают то же самое даже с bools. ||и &&ярлык, то есть второй аргумент не является операндом, если первый операнд true(соответственно falseдля &&). |, &, |=И &=всегда оценивают оба операнда.
Ники
4
это не отвечает на вопрос, почему && = и || = не являются операторами C ++.
Thang
9
Это не безопасно использовать &=для левой стороны типа bool, потому что это вполне возможно для правой руки , чтобы быть другого типа , чем bool(например, islowerили другой функция STDLIB С , который возвращает отличное от нуля для истинного значения). Если бы у нас было гипотетическое &&=, это, вероятно, заставило бы правую часть преобразоваться в bool, чего &=нет. Другими словами, в bool b = true; b &= 2;результате b == false.
Энгью больше не гордится СО
2
@Antonio Жесткая толпа. 😝
Конрад Рудольф
44

&&и &имеют другую семантику: &&не будет оценивать второй операнд, если первый операнд false. т.е. что-то вроде

flag = (ptr != NULL) && (ptr->member > 3);

безопасно, но

flag = (ptr != NULL) & (ptr->member > 3);

нет, хотя оба операнда относятся к типу bool.

То же самое относится и к &=и |=:

flag = CheckFileExists();
flag = flag && CheckFileReadable();
flag = flag && CheckFileContents();

будет вести себя иначе, чем:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();
Niki
источник
35
на мой взгляд, это еще одна причина иметь && =. = P
Kache
4
Хотя на самом деле это не ответ.
Catskul
27

Короткий ответ

Все операторы +=, -=, *=, /=, &=, |=... являются арифметическими и обеспечивают такое же ожидание:

x &= foo()  // We expect foo() be called whatever the value of x

Однако операторы &&=и ||=будут логичными, и эти операторы могут быть подвержены ошибкам, потому что многие разработчики ожидают, что они foo()будут всегда вызваны x &&= foo().

bool x;
// ...
x &&= foo();           // Many developers might be confused
x = x && foo();        // Still confusing but correct
x = x ? foo() : x;     // Understandable
x = x ? foo() : false; // Understandable
if (x) x = foo();      // Obvious
  • Неужели нам действительно нужно сделать C / C ++ еще более сложным, чтобы получить ярлык x = x && foo()?

  • Неужели мы действительно хотим еще больше запутать загадочное утверждение x = x && foo()?
    Или мы хотим написать осмысленный код вроде if (x) x = foo();?


Длинный ответ

Пример для &&=

Если &&=оператор был доступен, то этот код:

bool ok = true; //becomes false when at least a function returns false
ok &&= f1();
ok &&= f2(); //we may expect f2() is called whatever the f1() returned value

эквивалентно:

bool ok = true;
if (ok) ok = f1();
if (ok) ok = f2(); //f2() is called only when f1() returns true

Этот первый код подвержен ошибкам, потому что многие разработчики думают, что f2()он всегда вызывается независимо от f1()возвращаемого значения. Это похоже на запись, bool ok = f1() && f2();где f2()вызывается, только когда f1()возвращается true.

  • Если разработчик действительно хочет, f2()чтобы его вызывали только при f1()возврате true, следовательно, второй код выше менее подвержен ошибкам.
  • Иначе (разработчик хочет, f2()чтобы его всегда вызывали) &=достаточно:

Пример для &=

bool ok = true;
ok &= f1();
ok &= f2(); //f2() always called whatever the f1() returned value

Более того, компилятору проще оптимизировать этот код выше, чем код ниже:

bool ok = true;
if (!f1())  ok = false;
if (!f2())  ok = false;  //f2() always called

Сравните &&и&

Мы можем задаться вопросом, дают ли операторы &&и &тот же результат при применении к boolзначениям?

Давайте проверим, используя следующий код C ++:

#include <iostream>

void test (int testnumber, bool a, bool b)
{
   std::cout << testnumber <<") a="<< a <<" and b="<< b <<"\n"
                "a && b = "<< (a && b)  <<"\n"
                "a &  b = "<< (a &  b)  <<"\n"
                "======================"  "\n";
}

int main ()
{
    test (1, true,  true);
    test (2, true,  false);
    test (3, false, false);
    test (4, false, true);
}

Вывод:

1) a=1 and b=1
a && b = 1
a &  b = 1
======================
2) a=1 and b=0
a && b = 0
a &  b = 0
======================
3) a=0 and b=0
a && b = 0
a &  b = 0
======================
4) a=0 and b=1
a && b = 0
a &  b = 0
======================

Вывод

Поэтому YES мы можем заменить &&на &для boolзначений ;-)
Так что лучше использовать &=вместо &&=.
Мы можем считать &&=бесполезным для логических значений.

То же самое для ||=

оператор |=также менее подвержен ошибкам, чем||=

Если разработчик хочет, чтобы f2()его вызывали только при f1()возврате false, а не:

bool ok = false;
ok ||= f1();
ok ||= f2(); //f2() is called only when f1() returns false
ok ||= f3(); //f3() is called only when f1() or f2() return false
ok ||= f4(); //f4() is called only when ...

Я советую следующую более понятную альтернативу:

bool ok = false;
if (!ok) ok = f1();
if (!ok) ok = f2();
if (!ok) ok = f3();
if (!ok) ok = f4();
// no comment required here (code is enough understandable)

или, если вы предпочитаете стиль единой линии :

// this comment is required to explain to developers that 
// f2() is called only when f1() returns false, and so on...
bool ok = f1() || f2() || f3() || f4();
olibre
источник
13
Что, если я действительно хочу такого поведения? Правое выражение не выполняется, если левое выражение неверно. Досадно писать переменные два раза, напримерsuccess = success && DoImportantStuff()
Niklas R
1
Мой совет - писать if(success) success = DoImportantStuff(). Если бы оператор success &&= DoImportantStuff()был разрешен, многие разработчики подумали бы, что DoImportantStuff()он всегда вызывается независимо от значения success. Надеюсь, это ответит на то, что вам интересно ... Я также улучшил многие части своего ответа. Скажите, пожалуйста, теперь мой ответ понятнее? (о цели вашего комментария) Ура, до встречи ;-)
olibre
9
«Если бы такое утверждение success &&= DoImportantStuff()было разрешено, многие разработчики подумали бы, что DoImportantStuff()оно всегда называется, независимо от ценности успеха». Вы можете сказать это об этом if (success && DoImportantStuff()). Пока они помнят логику синтаксиса if, у них не должно возникнуть проблем &&=.
pilkch 05
2
Я не понимаю, как люди могут предполагать, что f1()всегда дает оценку, ok &&= f(1) но не будут считать, что она всегда дает оценку ok = ok && f(1). Мне это кажется столь же вероятным.
einpoklum
1
На самом деле я ожидаю, v1 += e2что это синтаксический сахарный эквивалент v1 = v1 + e1переменной v1 и выражения e2. Просто сокращенное обозначение, вот и все.
einpoklum