В C++ есть логические операции !a, a && b, a || b, применимые к значениям true/false.
И у него есть побитовые операции, применимые ко всем (целочисленным?) типам, ~a, a & b, a | b, a ^ b, (a << N, a >> N).
Вместе с операторами мутации на месте a &= b, a |= b, a ^= b, (a <<= b, a >>= b).
Что меня смущает, так это то, что побитовые операции могут действовать на биты bool, которые вообще не являются частью значения true/false.
Даже делая ненужные битовые операции.
Я предполагаю, что последний факт делает побитовый сдвиг неопределенным для bool, и это заставляет меня сомневаться в том, что все побитовые операции вообще допустимы.
Итак, эквивалентны ли они для логических типов? ~a == !a, a && b == a & b, a || b == a | b?
По какой-то причине (здесь не буду спрашивать) нет логических операторов мутации «на месте» (т. Е. a &&= b, a ||= b).
Это вдохновляет на эти вопросы: действительны ли эти другие побитовые операции для самих логических значений? всегда ли a &= b и a |= b эквивалентны a = a && b и a = a || b соответственно?**
Несколько примеров: https://godbolt.org/z/hcccG8c9o
@selbie, я экстраполирую, что в любой побитовой операции bool повышается до int (почему не char?), и это дает возможность оценить ответ на мои вопросы. Что, я думаю, положительно во всех случаях, но я плохо принимаю во внимание крайние случаи.
за исключением режима короткого замыкания.
пробы и ошибки не должны быть последними, но они могут стать первым шагом к пониманию. Я не совсем понимаю ваши утверждения. Вы можете сравнить bit и bool бок о бок: godbolt.org/z/jsE6bdhqd. То, что вы сказали, продвижение к целочисленному портит ~
да я вообще не понимаю зачем. Дело даже не в преобразовании обратно в bool. godbolt.org/z/nd1h1P1M4
@alfC -- re "почему бы и нет char -- все целочисленные типы, которые меньше int, повышаются до int (или unsigned int, но это не имеет значения здесь), когда они используются в выражениях. Таким образом, повышение bool до char было бы просто дополнительным шаг на пути к int.





(Для простоты я буду предполагать C++20, т. е. представление целочисленных типов с дополнением до двух. То, что я пишу, технически может не выполняться до C++20 для всех теоретически возможных соответствующих представлений целочисленных типов.)
Что меня смущает, так это то, что побитовые операции могут воздействовать на биты логического значения, которые вовсе не являются частью значения true/false.
Встроенные побитовые операции имеют обычные арифметические преобразования , которые сначала применяются к их операндам. Для целочисленных операндов, которые подразумевают целочисленные повышения: операнды с рангом преобразования ниже int сначала преобразуются в int (или unsigned int или целочисленный тип более высокого ранга, если int не может содержать все значения исходного типа).
Поощрение всегда оставляет исходное значение без изменений. В случае bool, true отображается на 1 и false на 0. Таким образом, результат после продвижения будет иметь младший значащий бит либо установленным, либо неустановленным, в то время как все остальные биты не будут установлены. Кроме того, всегда есть как минимум 15 таких битов, потому что int должен иметь ширину не менее 16 бит.
Как следствие, ~a == !a всегда ложно. ~ установит эти дополнительные биты, а продвижение !a — нет. Однако bool(~a) == bool(!a) является true, только если a является false, потому что с установленными дополнительными битами bool(~a) всегда true.
a && b == a & b всегда верно, потому что дополнительные биты будут равны нулю при применении &.
a || b == a | b всегда верно, потому что дополнительные биты снова останутся нулевыми.
Однако, если a и b являются реальными выражениями, то есть еще одно отличие в том, что логические операторы сокращают вычисление, а побитовые — нет. Поэтому побочные эффекты могут быть разными.
Кроме того, результатом логических операций будет bool, а результатом побитовых — int. Поэтому они не являются взаимозаменяемыми, как могут, например. влияют на разрешение перегрузки, где используется их результат.
Кроме того, это относится только к встроенным операторам. Если задействованы какие-либо перегруженные операторы, ничего не гарантируется.
У меня было ощущение, что произошел какой-то нелогичный случай. ~a не эквивалентно !a очень похоже.
«Если задействованы какие-либо перегруженные операторы, ничего не гарантируется». , это и есть источник моего вопроса, он заключался в том, чтобы реализовать перегрузку операторов логически (и/или побитово) согласованным образом.
@alfC С точки зрения стоимости вы в основном хороши с & и |, но не с ~. Тем не менее, поведение короткого замыкания важно, как и тот факт, что составные операторы присваивания оценивают левую часть только один раз.
@alfC Я бы не стал пытаться изменить значение побитовых и логических операторов, чтобы они соответствовали одной парадигме. Это очень нелогично. Либо сохраните стандартное значение, либо не перегружайте те, которые не соответствуют парадигме, которой вы хотите следовать.
Я часто злоупотребляю перегрузкой операторов (включая (унарные) &, ~ и ,) (я могу указать вам на них). Но сколько бы я ни делал, я никогда не перегружаю &&, ||, потому что считаю это «невозможным»/«всегда неправильным», потому что логические сокращения нельзя имитировать путем перегрузки операторов.
перегрузка операторов — крутая функция, пока вы не обнаружите, что не перегрузка операторов тоже довольно крутая ;)
@ 463035818_is_not_a_number, не могу не согласиться. Оба классные. Если вы собираетесь это сделать, вы должны сделать это полностью: gitlab.com/correaa/boost-multi/-/blob/master/…
@463035818_is_not_a_number Очевидно &(&*((~A).begin()+1))->operator()(2) == &A[2][1], godbolt.org/z/YKeK85ofo
Это отвечает на ваш вопрос: stackoverflow.com/q/32800866/104458