我希望操作员能够短路true
或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..
}
我的黑客解决方案是使用
if
-else if
块和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
。不过,您可以使用表达式模板完全完成您要问的事情,我将向您展示如何做。
但是有一些限制:
您无法创建新的运算符,因此必须选择两个要重载的运算符:一个用于叶比较,一个用于短路组合。
(如果您确实需要,可以使用一个运算符,但这会(甚至更多)令人困惑,并且需要很多括号)
当两个操作数都是基元时,您实际上无法做到这一点。如果可以,您的代码将如下所示:
bool firstIsEarlier(std::string first, std::string second){
return first[0]^second[0] <<= first[1]^second[1] <<= first[2]^second[2];
}
但是实际上您需要将
char
包装在一些值容器中才能正常工作。首先,我们需要一个简单的三态类型。我们可以列举一下:
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/