带三元回路和短路的操作员
Operator with Ternary Return and Short-Circuit
我想要一个能够使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;
}
或者更一般地使用CCD_ 18。
不过,使用表达式模板,您可以完全按照要求执行,我将向您展示如何执行。
不过也有一些限制:
不能创建新的运算符,所以必须选择两个要重载的运算符:一个用于叶比较,一个用于短路组合。
(如果你真的想的话,你可以使用一个运算符,但它会(甚至更)令人困惑,需要很多括号)
当两个操作数都是基元时,就不能真正做到这一点。如果可以的话,你的代码看起来像:
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);
}
对于非叶CCD_ 24:
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++ 与操作员不匹配<<
- 操作员C++的模棱两可的过载
- C++中>>操作员过载时出现问题?
- NaN 上的宇宙飞船操作员
- 比根<操作员
- SFINAE不能防止模棱两可的操作员过载吗?
- 什么是现实中的"endl"(或任何输出操纵器)?它是如何实现的,它如何与操作员<<一起工
- 为什么"delete"操作员给我访问权限冲突
- 为什么使操作员成为新的专用会打破 std::shared_ptr?
- 为什么当你将函数的返回值乘以零时它不会短路?
- 在这种情况下是私有的吗?试图使操作员<<过载
- C++操作员过载>>
- 是否有 C++20 浮点数的包装器,使我能够默认宇宙飞船操作员?
- 与异常处理程序中的操作员<<不匹配
- 复印作业操作员说明
- 短路运算符在enable_if
- 友谊和操作员+=重载
- 过载操作员短路 && 和 ||在C++17
- 带三元回路和短路的操作员