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

前端 未结 9 1743
礼貌的吻别
礼貌的吻别 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: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 
    
    struct S
    {
      bool val;
    
      explicit S(int i) : val(i) {}  
      explicit S(bool b) : val(b) {}
    
      template 
      S (const Expr & expr)
       : val(evaluate(expr).val)
      { }
    
      template 
      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 
    S evaluate(const Expr & expr) 
    {
      return expr.eval();
    }
    
    struct And 
    {
      template 
      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 
      S operator ()(const LExpr & l, const RExpr & r) const
      {
        const S & temp = evaluate(l);
        return temp? temp : logical_or(temp, evaluate(r));
      }
    };
    
    
    template 
    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 
    auto operator && (const LExpr & lhs, const S & rhs)
    {
      return Expr (lhs, rhs);
    }
    
    template 
    auto operator && (const LExpr & lhs, const Expr & rhs)
    {
      return Expr> (lhs, rhs);
    }
    
    template 
    auto operator || (const LExpr & lhs, const S & rhs)
    {
      return Expr (lhs, rhs);
    }
    
    template 
    auto operator || (const LExpr & lhs, const Expr & rhs)
    {
      return Expr> (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;
    }
    

提交回复
热议问题