简体   繁体   English

使用比较运算符时检查多个值

[英]Check for multiple values when using comparison operators

I've always been under the impression that for any comparison statement, ie X == Y or X != Y is the format, and you chain statements together with && or || 我总是认为对于任何比较语句,即X == YX != Y是格式,并且您将语句与&&||一起链接 .

Is there not some way to write X == (Y || Z) instead of X == Y || X == Z 是不是有办法写X == (Y || Z)而不是X == Y || X == Z X == Y || X == Z ? X == Y || X == Z

Edit : Since it has been established that this is not possible to do cleanly, how else could it be done? 编辑 :既然已经确定这不可能干净利落,那怎么办呢?

#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;
}

proof of compilation . 汇编证明 Xeo also observes that std::any_of , std::all_of and std::none_of may have been useful, depending on your actual needs and desires. Xeo还观察到std::any_ofstd::all_ofstd::none_of可能非常有用,具体取决于您的实际需求和愿望。

There's no clean way to do what you ask in C++. 没有干净的方法来做你在C ++中提出的要求。

What trips many people up is that X == (Y || Z) may be a legal expression and the compiler will not complain. 让很多人感到高兴的是, X == (Y || Z)可能是一个合法的表达式,编译器不会抱怨。 It will just be a bug. 这只是一个bug。 Each C++ statement must evaluate to true/false on its own and the operators just string them together. 每个C ++语句必须自己评估为true / false,并且运算符只是将它们串在一起。 What you're suggesting would require some intrinsic list structure. 你的建议需要一些内在的列表结构。 Many languages have that (like Python), but C++ does not. 许多语言(如Python),但C ++没有。

With operator overloading, you might be able to get the exact syntax that you want. 通过运算符重载,您可以获得所需的确切语法。 But, as Adam points out, that could lead to excluding valid expressions. 但是,正如亚当指出的那样,这可能导致排除有效的表达。

Below is a template with operator overloading, a template function, and a macro to achieve a syntax similar to Mooing Duck's nicer solution, but without requiring C++11, and allowing the use of the || 下面是一个带有运算符重载,模板函数和宏的模板,以实现类似于Mooing Duck更好的解决方案的语法,但不需要C ++ 11,并允许使用|| operator to denote the "haystack" collection. 运算符表示“haystack”集合。

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)

Then, the following program "works": 然后,以下程序“有效”:

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;
    }
}

There might be a way to achieve what you want with expression templates . 可能有一种方法可以通过表达式模板实现您想要的效果。 Below a sketch of how to approach this (does not compile, lots of details missing, caveat lector). 下面是如何处理这个问题的草图 (不编译,缺少许多细节,告诫者)。 First you setup a class template to represent logical values and define some operators over them. 首先,您设置一个类模板来表示逻辑值,并在它们上面定义一些运算符。

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

Because function templates can't be partially specialized, you delegate your actual work to class templates. 由于功能模板不能部分专门化,因此您将实际工作委托给类模板。

// 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()); }
}

Obviously, there is a lot of heavy template machinery involved to get natural C++ syntax for what you want. 显然,有很多繁重的模板机制可以为你想要的东西获得自然的C ++语法。 But with a lot of effort and reading up you might eventually get something nice. 但是经过大量的努力和阅读,你最终可能会得到一些不错的东西。 (You'd have to define Atomic, OpAnd, OpOr, setup representations holding the first and second branches of subexpression etc. etc.) (您必须定义Atomic,OpAnd,OpOr,设置子表达式的第一和第二分支的设置表示等。)

However, even if you would succeed, you would get really weird semantics in your scheme. 但是,即使你成功了,你的方案也会得到非常奇怪的语义 What you are proposing is require == to be left-distributive over || 你提出的是要求==|| 分配左派 or && . && Ie to parse 即解析

X == (Y @OP Z) 

as

(X == Y) @OP (X == Z)

with @OP equal to && or || @OP等于&&|| . I think it would be natural to require that == remains symmetric . 我认为要求==保持对称是很自然的。 This would require you also to impose right-distributivity of == over && and || 这还需要你强加== over &&|| 权利分配 . Ie to parse 即解析

(X @OP Y) == Z 

as

(X == Z) @OP (Y == Z)

However, if you combine the two with expression as (A @OP1 B) == (C @OP2 D) , you get logical inconsistencies. 但是,如果将两者结合使用表达式(A @OP1 B) == (C @OP2 D) ,则会出现逻辑不一致。 Eg the result is depending on the order in which you apply left-distribution and right-distribution. 例如,结果取决于您应用左分布和右分布的顺序。

Left-then-right : 左右

(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))

Right-then-left : 右 - 左

(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))

In both cases, the same 4 pairs of elements are being compared, but the way they are being propagated up the expression tree is subtly different. 在这两种情况下,正在比较相同的4对元素,但它们在表达式树上向上传播的方式略有不同。 If @OP1 and @OP2 are the same, then you can flatten the entire tree and re-order the terms to get a unique result. 如果@OP1@OP2相同,那么您可以展平整个树并重新排序这些术语以获得唯一的结果。 , it works out OK if you use the same operators on both sides of the == , because both && and || ,如果你在==两边使用相同的运算符,那就好了,因为&&|| are associative as well as commutative . 联想的 ,也是可交换的

But for mixed operators the resulting expressions will in general be different. 但对于混合运算符,结果表达式通常会有所不同。

UPDATE : as mentioned in the comments to this and other answers, you also loose certain properties of built-in types. 更新 :正如对此答案和其他答案的评论中所述,您还会松开内置类型的某些属性。 First, the short-circuit rules, which are not obeyed by overloaded operators. 首先是短路规则,超载运营商不遵守这些规则。 For logical expressions not involving pointer dereferencing or other resource access ( if(p && p->value()) or if(file && file.open()) etc.) this would not influence correctness but only efficiency. 对于不涉及指针解除引用或其他资源访问的逻辑表达式( if(p && p->value())if(file && file.open())等),这不会影响正确性,只会影响效率。 Otherwise be careful! 否则小心! Second, it was also mentioned that mixed evaluations of constants/expressions would go wrong. 其次,还提到对常量/表达式的混合评估会出错。 This has a simple (but verbose) fix: simply use std::integral_constant (or boost::mpl::int_ ) as a wrapper. 这有一个简单(但冗长)的修复:只需使用std::integral_constant (或boost::mpl::int_ )作为包装器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM