我一直认为对于任何比较语句,即X == Y
orX != Y
是格式,并且您将语句与&&
or链接在一起||
。
没有什么方法可以X == (Y || Z)
代替X == Y || X == Z
吗?
编辑:既然已经确定这是不可能干净地做到的,那还能怎么做呢?
我一直认为对于任何比较语句,即X == Y
orX != Y
是格式,并且您将语句与&&
or链接在一起||
。
没有什么方法可以X == (Y || Z)
代替X == Y || X == Z
吗?
编辑:既然已经确定这是不可能干净地做到的,那还能怎么做呢?
#include <algorithm>
#include <array>
#include <string>
#include <iostream>
#include <initializer_list>
template<class Type, class Next>
bool is_one_of(const Type& needle, const Next& next)
{return needle==next;}
template<class Type, class Next, class ... Rest>
bool is_one_of(const Type& needle, const Next& next, Rest... haystack)
{return needle==next || is_one_of(needle, haystack...);}
int main() {
std::string X, Y;
if (is_one_of(X, Y, "HI"))
std::cout << "it is!";
else
std::cout << "it isn't!";
return 0;
}
编译证明。 Xeo还观察到std::any_of
, std::all_of
并且std::none_of
可能很有用,具体取决于您的实际需求和愿望。
没有干净的方法可以完成您在 C++ 中提出的要求。
让很多人感到困惑的是,这X == (Y || Z)
可能是一个合法的表达方式,编译器不会抱怨。这将只是一个错误。每个 C++ 语句必须自行评估为真/假,而运算符只需将它们串在一起。您的建议需要一些内在的列表结构。许多语言都有(比如 Python),但 C++ 没有。
通过运算符重载,您可能可以获得所需的确切语法。但是,正如亚当指出的那样,这可能会导致排除有效表达式。
下面是一个带有运算符重载的模板、一个模板函数和一个宏,用于实现类似于 Mooing Duck 更好的解决方案的语法,但不需要 C++11,并允许使用||
运算符来表示“干草堆”集合。
template <typename T>
struct MultiOrComparable {
mutable std::set<T> vals;
const MultiOrComparable & operator || (T v) const {
vals.insert(v); return *this;
}
bool operator == (T v) const { return vals.find(v) != vals.end(); }
};
template <typename T>
MultiOrComparable<T> MultiOrComparableStart (T) {
return MultiOrComparable<T>();
}
#define IsOneOf(x, y) ((MultiOrComparableStart(x)||y) == x)
然后,以下程序“工作”:
enum Foo { A, B, C, D };
int
main ()
{
if (!IsOneOf(A, B || C || D)) {
std::cout << "!=" << std::endl;
}
if (IsOneOf('a', 'x' || 'y' || 'z' || 'a')) {
std::cout << "==" << std::endl;
}
}
可能有一种方法可以使用表达式模板来实现您想要的。下面是如何处理这个问题的草图(不编译,缺少很多细节,警告讲师)。首先,您设置一个类模板来表示逻辑值并在它们上定义一些运算符。
template<typename T, typename Type = Atomic<T> >
class Logical;
template<typename T, typename E1, typename E2>
Logical<T, OpOr<T, E1, E2> > operator||(Logical<T, E1> lhs, Logical<T, E2> rhs);
template<typename T, typename E1, typename E2>
Logical<T, OpAnd<T, E1, E2> > operator&&(Logical<T, E1> lhs, Logical<T, E2> rhs);
template<typename T, typename E1, typename E2>
Logical<T, OpEq<T, E1, E2> > operator==(Logical<T, E1> lhs, Logical<T, E2> rhs)
{ return OpEq<T, E1, E2>()(lhs, rhs); } // delegate to class template
因为函数模板不能部分特化,所以您将实际工作委托给类模板。
// primary template
template<typename T, typename E1, typename E2> class OpEq;
// specialization for atomic comparisons
template<typename T>
class OpEq<T, Atomic<T>, Atomic<T> >
{
bool operator()(Atomic<T> lhs, Atomic<T> rhs)
{ return lhs == rhs; }
}
// apply distributive rule
template<typename T>
class OpEq<T, Atomic<T>, OpOr<T, Atomic<T>, Atomic<T> > >
{
bool operator()(Atomic<T> lhs, OpOr<T, Atomic<T>, Atomic<T> > rhs)
{ return (lhs == rhs.first()) && (lhs == rhs.second()); }
}
显然,要获得所需的自然 C++ 语法,需要使用大量繁重的模板机制。但是通过大量的努力和阅读,您最终可能会得到一些不错的东西。(您必须定义 Atomic、OpAnd、OpOr、设置表示持有子表达式的第一个和第二个分支等等等)
然而,即使你成功了,你的方案中也会出现非常奇怪的语义。您所提议的是要求在or==
上进行左分配。即解析||
&&
X == (Y @OP Z)
作为
(X == Y) @OP (X == Z)
@OP
等于&&
或||
。_ 我认为要求==
保持对称是很自然的。这将要求您还施加over和的右分配。即解析==
&&
||
(X @OP Y) == Z
作为
(X == Z) @OP (Y == Z)
但是,如果将两者与表达式 as 结合使用,则会出现(A @OP1 B) == (C @OP2 D)
逻辑上的不一致。例如,结果取决于您应用左分布和右分布的顺序。
从左到右:
(A @OP1 B) == (C @OP2 D)
((A @OP1 B) == C) @OP2 ((A @OP1 B) == D)
((A == C) @OP1 (B ==C)) @OP2 ((A == D) @OP1 (B == D))
从右到左:
(A @OP1 B) == (C @OP2 D)
(A == (C @OP2 D)) @OP1 (B == (C @OP2 D))
((A == C) @OP2 (A == D)) @OP1 ((B == C) @OP2 (B == D))
在这两种情况下,都在比较相同的 4 对元素,但它们在表达式树上传播的方式略有不同。如果@OP1
和@OP2
相同,那么您可以展平整个树并重新排序术语以获得独特的结果。,如果在 的两边使用相同的运算符,就可以了==
,因为&&
和||
都是关联的和可交换的。
但是对于混合运算符,结果表达式通常会有所不同。
更新:如对此答案和其他答案的评论中所述,您还失去了内置类型的某些属性。首先,重载运算符不遵守的短路规则。对于不涉及指针取消引用或其他资源访问(if(p && p->value())
或if(file && file.open())
等)的逻辑表达式,这不会影响正确性,而只会影响效率。否则要小心!其次,还提到常量/表达式的混合评估会出错。这有一个简单(但冗长)的修复:只需使用std::integral_constant
(or boost::mpl::int_
) 作为包装器。