C++ Weekly - Ep 157 - Never Overload Operator && or ||

preview_player
Показать описание
☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟

Upcoming Workshop: Applied constexpr: The Power of Compile-Time Resources, C++ Under The Sea, October 10, 2024

T-SHIRTS AVAILABLE!

WANT MORE JASON?

SUPPORT THE CHANNEL

GET INVOLVED

JASON'S BOOKS

► C++23 Best Practices

► C++ Best Practices

JASON'S PUZZLE BOOKS

► Object Lifetime Puzzlers Book 1

► Object Lifetime Puzzlers Book 2

► Object Lifetime Puzzlers Book 3

► Copy and Reference Puzzlers Book 1

► Copy and Reference Puzzlers Book 2

► Copy and Reference Puzzlers Book 3

► OpCode Puzzlers Book 1


RECOMMENDED BOOKS

AWESOME PROJECTS

O'Reilly VIDEOS

Рекомендации по теме
Комментарии
Автор

The only situation where this would possibly make sense to overload && or || is when the objects carry some sort of inherent boolean "meaning" in which cases one can just implement `operator bool()`. Then, you could actually write `make_S(true) || make_S(false)` and it would implicitly convert to bool and short-circuit as expected.

jgreitemann
Автор

In case of expression templates I would say overloading logical operators is not a bad thing because those basically build a type. Final evaluation is going to provide short circuiting when needed.
Another example would be DSL which builds SQL queries out of normally looking C++ operations. I want && to translate into SQL's AND and || into OR and short circuiting is not intended as it is left to the SQL engine. But the goal is to make sure types are not messed up so && and || would not be called on non-boolean arguments, not to avoid evaluating the arguments (which are in this case just special objects representing DB columns)

pazdziochowaty
Автор

I have overloaded the && and || operators when I am comparing two objects with several members. It clarifies what you are doing for the next person and they can drill down into the function to see what's going on.

You loose short circuiting when you are comparing several members of a class need these conditions so lets make the code more readable for the next engineer.

redboomer
Автор

The problem is that the overloaded operator takes a pair of S. What you want to elide is the right hand side, which one (or more) function calls, which returns the S to be passed into the operator. To even enter the operator, where short circuiting would be possible, you need your const refs to the S-es, so both arguments must exist. The magic you would have to write up to actually short circuit the function calls would be to pass some function type along with the parameters, and only do the function calls within the operator. Which is really janky, and I don't know if it's possible without jankiness at the call site.

Tyranisaur
Автор

With C++17, the comma operator sequencing semantics still happen when overloaded. You still shouldn't do it because people also expect the value to match, but at least it's less bad than it was before.

danielrhouck
Автор

You can get the semantics of the built in operator though right? Just check them individually in the overloaded operator and return as soon as its violated.

ultimatesoup
Автор

There is actually a very limited scenario where you can do this, starting with C++17.
C++17 says that the order of evaluation of the arguments to overloaded operator&& follows the same rules as the built-in variant (i.e. left to right). If you stick a special type to the left of the expression (for which you've overloaded the operator&&), the compiler should call your operator for each individual sub-expression within the entire logical expression. You can then throw an exception from within this operator if the right hand expression is false (the left hand expression being your special type), thus "short circuiting" the rest of the expression.
Obviously, this can't be applied in most cases and has some quite significant drawbacks, but I found it to be an interesting solution, which I used to analyse complex (release) assert conditions:

bool* p = ...;
fancy_assert(p && *p); // the fancy_assert macro is able to decompose the condition and report which sub-expression failed.

hackitsoft
Автор

You could overload if you did not have any side effects (or your side effects don't matter, like printing to a log...)
You could overload them if you make the arguments lazy, meaning you only force the full evaluation of an unevaluated RHS if it is needed.
I also do this in DSLs where I'm constructing logical syntax trees. "Never" is just a bit too strong right?

Tupster
Автор

it's even worse order is not guaranteed!

Carutsu
Автор

It would be OK, if C++ had a way to do lazy evaluation with function parameters. I propose an expression type specifier(call by name). The syntax is similar to the reference type specifiers, the expression is evaluated. When the statement/expression is evaluated, in which the parameter (passed as expression/name) is referred to:

Bool operator&&(Bool a, Bool# b) {
if(a.value) return a;
return b; // b is evaluated
};

cmdlp
Автор

This (even tho it isn't?) really looks like a bug. Why wouldn't the operands get optimized away with the value they return? EDIT: They do. It's just the puts inside the operator that's forcing the compiler not to... This should be a warning: Function "puts" (or whatever) inside logical operator will make all operands be evaluated all the time. Or "Will prevent logical operands from being optimized".

DamianReloaded
Автор

So, what is the best practice for implementing three-value logic semantics? And overloading operator comma is quite cool; you can provide lisp-style calling syntax for C++
(puts, (cons, "hello ", "world))

q_rsqrt
Автор

But what if you don't care about short-circuiting? I mean if you don't need the performance benefits and you don't have any side-effects, this shouldn't matter, right? Either way, isn't this a problem with the C++-Standard? I mean comparing two objects should behave the same like primitive types and simply recommending against using an otherwise useful feature of the language shouldn't be normal.

pcfreak
Автор

There are enough coding standards that make the short cut illegal. You lose the side effect of get_bool() due to short circuiting and this is most likely is either a bug or at least a design issue.
Intention probably did not show up high on the list when analyzing the usage of short-circuit evaluation in the codebases which have resulted in those standards.
So from that point of view it would be very beneficial to overload && and || - Birth of the 'avoid raw bool' idiom?.

Ridiculan