8

I'm trying to implement my own boolean class, but cannot replicate native semantics for &&. The following contrived code demonstrates the issue:



    #include <iostream>>

    class MyBool {
    public:
        bool theValue;
        MyBool() {}
        MyBool(bool aBool) {theValue = aBool;}
        MyBool operator&& (MyBool aBool) {return theValue && aBool.theValue;}
    };

    bool f1() {std::cout << "   First\n"; return false;}
    bool f2() {std::cout << "   Second\n"; return false;}

    int main(int argc, char** argv) {
        std::cout << "Native &&\n";
        f1() && f2();
        std::cout << "Overloaded &&\n";
        MyBool(f1()) && MyBool(f2());
        return 0;
}

When compiled and run, the result is:


    Native &&
       First
    Overloaded &&
       Second
       First

In other words, && on bools is lazy (as any C++ programmer would expect) but the overloaded && isn't (as this C++ programmer at least didn't expect).

Is there a way to make overloaded && lazy? I can find various full-on lazy evaluation schemes to provide Haskell-like functionality, but they seem like complete overkill for my use case.

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
user2285449
  • 83
  • 1
  • 3
  • 3
    By lazy you mean short circuit. Overloaded operators doesn't have short circuiting. – Rapptz Apr 16 '13 at 08:10
  • This problem is exactly why it is recommended not to overload the `&&` operator. – Martin York Apr 16 '13 at 08:52
  • Note that the reason the overloaded operator cannot short-circuit is that `MyBool aBool` is passed into it as a parameter. So `MyBool(f2())` needs to be computed before the implementation of `operator&&` gets a chance to run. In theory you could write a template `operator&&` overload whose RHS is a functor that returns `MyBool`. Then call that functor only if the LHS evaluates true. Then in C++11 callers could write a lambda: `MyBool(f1()) && []() { return MyBool(f2()); }`. But in practice, don't do that -- lazy evaluation is pretty ugly when you have to explicitly specify it on every call. – Steve Jessop Apr 16 '13 at 09:16

4 Answers4

17

You should not overload bool operator&&, since you lose short circuit evaluation, as you have discovered.

The correct approach would be to give your class a bool conversion operator

class MyBool {
 public:
  bool theValue;
  MyBool() {}
  MyBool(bool aBool) : theValue(aBool) {}
  explicit operator bool() { return theValue; }
};

Note that explicit conversion operators require C++11 compliance. If you do not have this, have a look at the safe bool idiom.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • 1
    +1, but it should be noted that `explicit operator`s are a C++11 feature, and C++03 requires the Safe Bool idiom. – greyfade Apr 16 '13 at 08:15
6

Is there a way to make overloaded && lazy?

No.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
2

You can make almost anything evaluate lazily with the expression template idiom, including but not limited to the operators whose built-in versions short-circuit. But that's more work than you need for this one case, since then your MyBool class would require a lot more code.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • "You can make almost anything evaluate lazily" - that's misleading, as this approach only works for cooperating user-defined types. For example, for any `T* p;` and `myBool && ++p`, you can't short-circuit the increment. – Tony Delroy Apr 18 '13 at 01:20
0

If you really want short-circuiting and are willing to sacrifice the operator syntax, you can rename your operator&& method to _and, define an AND() macro, and write AND(x,y) instead of x&&y.

#define AND(x,y) (x._and(x.theValue ? y : MyBool(false)))

With some macro hacks you can have AND() accept a variable number of parameters.

The _and() method here is not intended to be used "publicly" here but must be declared public since you can't friend a macro.

For something as simple as your MyBool class, this is probably unnecessary. But if you need your operator&& to have special side-effects like updating some state on this, then this gets the job done.

Community
  • 1
  • 1
dshin
  • 2,354
  • 19
  • 29