Is there actually a reason why overloaded && and || don't short circuit?

前端 未结 9 1698
礼貌的吻别
礼貌的吻别 2020-11-28 21:54

The short circuiting behaviour of the operators && and || is an amazing tool for programmers.

But why do they lose this behaviour w

相关标签:
9条回答
  • 2020-11-28 22:41

    but the operators for bool have this behaviour, why should it be restricted to this single type?

    I just want to answer this one part. The reason is that the built-in && and || expressions are not implemented with functions as overloaded operators are.

    Having the short-circuiting logic built-in to the compiler's understanding of specific expressions is easy. It's just like any other built-in control flow.

    But operator overloading is implemented with functions instead, which have particular rules, one of which is that all the expressions used as arguments get evaluated before the function is called. Obviously different rules could be defined, but that's a bigger job.

    0 讨论(0)
  • 2020-11-28 22:45

    Lambdas is not the only way to introduce laziness. Lazy evaluation is relatively straight-forward using Expression Templates in C++. There is no need for keyword lazy and it can be implemented in C++98. Expression trees are already mentions above. Expression templates are poor (but clever) man's expression trees. The trick is to convert the expression into a tree of recursively nested instantiations of the Expr template. The tree is evaluated separately after construction.

    The following code implements short-circuited && and || operators for class S as long as it provides logical_and and logical_or free functions and it is convertible to bool. The code is in C++14 but the idea is applicable in C++98 also. See live example.

    #include <iostream>
    
    struct S
    {
      bool val;
    
      explicit S(int i) : val(i) {}  
      explicit S(bool b) : val(b) {}
    
      template <class Expr>
      S (const Expr & expr)
       : val(evaluate(expr).val)
      { }
    
      template <class Expr>
      S & operator = (const Expr & expr)
      {
        val = evaluate(expr).val;
        return *this;
      }
    
      explicit operator bool () const 
      {
        return val;
      }
    };
    
    S logical_and (const S & lhs, const S & rhs)
    {
        std::cout << "&& ";
        return S{lhs.val && rhs.val};
    }
    
    S logical_or (const S & lhs, const S & rhs)
    {
        std::cout << "|| ";
        return S{lhs.val || rhs.val};
    }
    
    
    const S & evaluate(const S &s) 
    {
      return s;
    }
    
    template <class Expr>
    S evaluate(const Expr & expr) 
    {
      return expr.eval();
    }
    
    struct And 
    {
      template <class LExpr, class RExpr>
      S operator ()(const LExpr & l, const RExpr & r) const
      {
        const S & temp = evaluate(l);
        return temp? logical_and(temp, evaluate(r)) : temp;
      }
    };
    
    struct Or 
    {
      template <class LExpr, class RExpr>
      S operator ()(const LExpr & l, const RExpr & r) const
      {
        const S & temp = evaluate(l);
        return temp? temp : logical_or(temp, evaluate(r));
      }
    };
    
    
    template <class Op, class LExpr, class RExpr>
    struct Expr
    {
      Op op;
      const LExpr &lhs;
      const RExpr &rhs;
    
      Expr(const LExpr& l, const RExpr & r)
       : lhs(l),
         rhs(r)
      {}
    
      S eval() const 
      {
        return op(lhs, rhs);
      }
    };
    
    template <class LExpr>
    auto operator && (const LExpr & lhs, const S & rhs)
    {
      return Expr<And, LExpr, S> (lhs, rhs);
    }
    
    template <class LExpr, class Op, class L, class R>
    auto operator && (const LExpr & lhs, const Expr<Op,L,R> & rhs)
    {
      return Expr<And, LExpr, Expr<Op,L,R>> (lhs, rhs);
    }
    
    template <class LExpr>
    auto operator || (const LExpr & lhs, const S & rhs)
    {
      return Expr<Or, LExpr, S> (lhs, rhs);
    }
    
    template <class LExpr, class Op, class L, class R>
    auto operator || (const LExpr & lhs, const Expr<Op,L,R> & rhs)
    {
      return Expr<Or, LExpr, Expr<Op,L,R>> (lhs, rhs);
    }
    
    std::ostream & operator << (std::ostream & o, const S & s)
    {
      o << s.val;
      return o;
    }
    
    S and_result(S s1, S s2, S s3)
    {
      return s1 && s2 && s3;
    }
    
    S or_result(S s1, S s2, S s3)
    {
      return s1 || s2 || s3;
    }
    
    int main(void) 
    {
      for(int i=0; i<= 1; ++i)
        for(int j=0; j<= 1; ++j)
          for(int k=0; k<= 1; ++k)
            std::cout << and_result(S{i}, S{j}, S{k}) << std::endl;
    
      for(int i=0; i<= 1; ++i)
        for(int j=0; j<= 1; ++j)
          for(int k=0; k<= 1; ++k)
            std::cout << or_result(S{i}, S{j}, S{k}) << std::endl;
    
      return 0;
    }
    
    0 讨论(0)
  • 2020-11-28 22:51

    The point is that (within the bounds of C++98) the right-hand operand would be passed to the overloaded operator function as argument. In doing so, it would already be evaluated. There is nothing the operator||() or operator&&() code could or could not do that would avoid this.

    The original operator is different, because it's not a function, but implemented at a lower level of the language.

    Additional language features could have made non-evaluation of the right-hand operand syntactically possible. However, they didn't bother because there are only a select few cases where this would be semantically useful. (Just like ? :, which is not available for overloading at all.

    (It took them 16 years to get lambdas into the standard...)

    As for the semantical use, consider:

    objectA && objectB
    

    This boils down to:

    template< typename T >
    ClassA.operator&&( T const & objectB )
    

    Think about what exactly you'd like to do with objectB (of unknown type) here, other than calling a conversion operator to bool, and how you'd put that into words for the language definition.

    And if you are calling conversion to bool, well...

    objectA && obectB
    

    does the same thing, now does it? So why overload in the first place?

    0 讨论(0)
提交回复
热议问题