我想要一个能够对 true
进行短路评估的运算符(operator)或 false
但也有一个表明需要继续测试的返回。
例如,两个字符串之间的字典序字符串比较first
和 second
:
- 如果
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
这会给我一个正数 true
或 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#
一个很好的解决方案做这样的事情:
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
.
不过,您可以使用表达式模板完全完成您的要求,我会告诉您如何做。
不过有一些限制:
您无法创建新的运算符,因此您必须选择要重载的两个运算符:一个用于叶比较,一个用于短路组合。 p>
(如果您确实需要,您可以使用单个运算符,但这会(甚至更)令人困惑并且需要很多括号)
当两个操作数都是基元时,您就无法真正做到这一点。如果可以,您的代码将如下所示:
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/