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

标签 c++ boolean short-circuiting ternary

我想要一个能够对 true 进行短路评估的运算符(operator) 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..
}

我的 hack 解决方案是使用 if - else if block 和一个 int这会给我一个正数 truefalse 的负数, 一个 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# 一个很好的解决方案做这样的事情:

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

最佳答案

您可能应该通过

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

或更一般地使用 std::lexicographical_compare .

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

不过有一些限制:

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

    (如果您确实需要,您可以使用单个运算符,但这会(甚至更)令人困惑并且需要很多括号)

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

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

    但实际上你需要包装你的 char s 在一些值容器中以使其工作。


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

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

接下来,我们需要一些东西来代表我们的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会递归到左侧子树,这对于这个应用程序来说似乎不够优雅。

关于c++ - 具有三元返回和短路的运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26255599/

相关文章:

php - Javascript 是否像 PHP 一样支持短三元(而不是变体)?

带有嵌入式函数调用的 C++ 输出评估顺序

c++ - Boost 记录器链接问题

c++ - 如何在 C++ 或 Python 中验证图像文件的完整性?

java - 仅有时陷入 while 循环

c - 字符串怎么可能是 boolean 值?

C# 让程序等待按钮被按下

c# - 在 .NET 中依赖 && 短路安全吗?

c++ - 转换double为Local8Bit

mysql - Mysql语句中的逻辑表达式没有给出期望的结果