具有三元返回和短路的运算符

时间:2014-10-08 11:17:58

标签: c++ boolean short-circuiting ternary

我希望运营商能够在true false上对评估进行短路,但也需要一个表明需要继续测试的回报。


例如,两个字符串firstsecond之间的字典字符串比较:

  • 如果first[0] < second[0],我们可以通过返回true
  • 来结束比较
  • 否则,如果first[0] > second[0],我们可以通过返回false
  • 来结束比较
  • 否则,first[0] == second[0]成立,我们需要继续使用两个字符串的第二个字符。

简单的解决方案需要两个比较:

bool firstIsEarlier(std::string first, std::string second){
    if(first[0] < second[0]){
        return true;
    }else if(first[0] > second[0]){
        return false;
    }
    // first[0] == second[0] holds
    // continue with second character..
}

我的黑客解决方案是使用if - else if块和int,它会为true提供正数或{{1}为负数} {},false表示继续测试。

0

因此,您可以看到强制短路的唯一方法是列出bool firstIsEarlier(std::string first, std::string second){ if(int i = first[0] - second[0]){ return i < 0; } else if(i = first[1] - second[1]){ return i < 0; } else if(i = first[2] - second[2]){ return i < 0; } return false; } 中的每个条件。一个好的解决方案是让我在一条线上完成所有这一切并保持短路的方法。一个很好的解决方案是,如果有else if做这样的事情:

operator#

1 个答案:

答案 0 :(得分:2)

你可能 应该

来实现这个目标
bool firstIsEarlier(std::string first, std::string second) {
    return first < second;
}

或更一般地使用std::lexicographical_compare

您可以使用表达式模板完全完成您所要求的内容,我会告诉您如何。

但是有一些限制:

  1. 您无法创建新的运算符,因此您必须选择要重载的两个运算符:一个用于叶比较,一个用于短路组合。

    (如果你真的想要,你可以使用一个操作员,但它会(甚至更多)令人困惑,并且需要很多括号)

  2. 当两个操作数都是原语时,你无法真正做到这一点。如果可以,您的代码将如下所示:

    bool firstIsEarlier(std::string first, std::string second){
        return first[0]^second[0] <<= first[1]^second[1] <<= first[2]^second[2];
    }
    

    但实际上您需要将char包装在某个值容器中才能使其正常工作。


  3. 首先,我们需要一个简单的三态类型。我们可以列举它:

    enum class TriState {
        True = -1,
        Maybe = 0,
        False = 1
    };
    

    接下来,我们需要一些 thing 来表示我们的first[0]^second[0]叶子表达式,它会计算为我们的三态类型:

    template <typename LHS, typename RHS>
    struct TriStateExpr {
        LHS const &lhs_;
        RHS const &rhs_;
    
        TriStateExpr(LHS const &lhs, RHS const &rhs) : lhs_(lhs), rhs_(rhs) {}
    
        operator bool () const { return lhs_ < rhs_; }    
        operator TriState () const {
            return (lhs_ < rhs_ ? TriState::True :
                    (rhs_ < lhs_ ? TriState::False : TriState::Maybe)
                   );
        }
    };
    

    请注意,我们只需要为我们的类型使用operator< - 如果需要,我们可以将其概括为使用显式比较器。

    现在,我们需要表达式树的非叶部分。我强迫它从右到左表达式树,所以左手表达式总是一个叶子,右手表达式可以是一个叶子或一个完整的子树。

    template <typename LLHS, typename LRHS, typename RHS>
    struct TriStateShortCircuitExpr {
        TriStateExpr<LLHS, LRHS> const &lhs_;
        RHS const &rhs_;
    
        TriStateShortCircuitExpr(TriStateExpr<LLHS, LRHS> const &lhs, RHS const &rhs)
            : lhs_(lhs), rhs_(rhs)
        {}
    
        operator TriState () const {
            TriState ts(lhs_);
            switch (ts) {
            case TriState::True:
            case TriState::False:
                return ts;
            case TriState::Maybe:
                return TriState(rhs_);
            }
        }
    
        operator bool () const {
            switch (TriState(lhs_)) {
            case TriState::True:
                return true;
            case TriState::False:
                return false;
            case TriState::Maybe:
                return bool(rhs_);
            }
        }
    };
    

    现在,你需要一些语法糖,所以我们必须选择要重载的运算符。我将^用于叶子(理由是<顺时针旋转90度):

    template <typename LHS, typename RHS>
    TriStateExpr<LHS, RHS> operator^ (LHS const &l, RHS const &r) {
        return TriStateExpr<LHS, RHS>(l,r);
    }
    
    非叶子的

    <<=

    template <typename LLHS, typename LRHS, typename RLHS, typename RRHS>
    TriStateShortCircuitExpr<LLHS, LRHS, TriStateExpr<RLHS, RRHS>>
        operator<<= (TriStateExpr<LLHS, LRHS> const &l,
                     TriStateExpr<RLHS, RRHS> const &r) {
        return TriStateShortCircuitExpr<LLHS, LRHS, TriStateExpr<RLHS, RRHS>>(l, r);
    }
    
    template <typename LLHS, typename LRHS, typename... RARGS>
    TriStateShortCircuitExpr<LLHS, LRHS, TriStateShortCircuitExpr<RARGS...>>
        operator<<= (TriStateExpr<LLHS, LRHS> const &l,
                     TriStateShortCircuitExpr<RARGS...> const &r) {
        return TriStateShortCircuitExpr<LLHS, LRHS,
                                        TriStateShortCircuitExpr<RARGS...>>(l, r);
    }
    

    主要考虑因素是叶子运算符理想情况下应该具有更高的优先级,而非叶子运算符应该从右到左关联。如果您使用了从左到右的关联运算符,TriStateShortCircuitExpr::operator将递归左侧子树,这对于此应用程序来说似乎不太优雅。