【问题标题】:Check for multiple values when using comparison operators [duplicate]使用比较运算符时检查多个值[重复]
【发布时间】:2012-07-19 23:32:07
【问题描述】:

我一直认为对于任何比较语句,即X == YX != Y 是格式,并且您将语句与&&|| 链接在一起。

有没有什么办法可以写X == (Y || Z)而不是X == Y || X == Z

编辑:既然已经确定这是不可能做到干净利落的,那还能怎么做呢?

【问题讨论】:

  • 您可能有一个可变参数函数,将第一个参数作为 X,其余作为 Y、Z 等。取消了运算符语法,但仍然有效:if (areAllEqual (X, Y, Z, A, B, C))
  • 好问题,我也在想同样的事情..

标签: c++ operator-overloading logical-operators expression-templates


【解决方案1】:
#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 compilationXeo 还发现 std::any_ofstd::all_ofstd::none_of 可能有用,具体取决于您的实际需求和愿望。

【讨论】:

  • std::initializer_list&lt;E&gt; 应该可以解决问题。 ideone.com/UvwYV 请注意,这会复制操作数。
  • 这有相当多的性能开销,但是 - 一些分配,创建一个临时,循环数组。
  • @Xeo:我实际上已经编写了 99% 的代码,但它失败了,所以当我看到你的评论时,我放弃了。当我再次查看我的代码时,我发现它仍然有导致失败的template&lt;class T, unsigned L&gt;。感谢您的评论!
  • @nightcracker: 是的,它将XY 复制到初始化列表中,然后在最后销毁它们。
  • 另外,这不支持混合类型。
【解决方案2】:

在 C++ 中没有干净的方法来完成您所要求的操作。

让很多人感到困惑的是X == (Y || Z) 可能是一个合法的表达式,编译器不会抱怨。这将只是一个错误。 每个 C++ 语句必须自行评估为真/假,而运算符只需将它们串在一起。你的建议需要一些内在的列表结构。许多语言都有这个(比如 Python),但 C++ 没有。

【讨论】:

  • X == (Y || Z):根据我的理解,我可以看到 if (x == (4 || 5)) 然后评估为 if (x == true)。我绝对可以看出这会让新程序员感到困惑。
【解决方案3】:

通过运算符重载,您可能能够获得所需的确切语法。但是,正如 Adam 指出的那样,这可能会导致排除有效表达式。

下面是一个带有运算符重载的模板、一个模板函数和一个宏,以实现类似于 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;
    }
}

【讨论】:

    【解决方案4】:

    expression templates 可能有一种方法可以实现您想要的。下面是如何处理此问题的草图(无法编译,缺少很多细节,请注意讲师)。首先,您设置一个类模板来表示逻辑值并在它们上定义一些运算符。

    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、设置表示包含子表达式的第一个和第二个分支等。)

    但是,即使您成功了,您的方案中也会得到真正奇怪的语义。您的提议是要求 ==||&amp;&amp; 左分配。 IE。解析

    X == (Y @OP Z) 
    

    作为

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

    @OP 等于 &amp;&amp;||。我认为要求== 保持对称 是很自然的。这将要求您还对&amp;&amp;|| 施加==右分布。 IE。解析

    (X @OP Y) == Z 
    

    作为

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

    但是,如果将两者与表达式组合为(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 相同,那么您可以展平整个树并重新排序术语以获得唯一的结果。 ,如果您在== 的两侧使用相同的运算符,则可以,因为&amp;&amp;|| 都是关联 以及可交换

    但是对于混合运算符,结果表达式通常会有所不同。

    更新:正如 cmets 中提到的这个和其他答案,你也失去了内置类型的某些属性。首先,短路规则,重载运算符不遵守。对于不涉及指针取消引用或其他资源访问的逻辑表达式(if(p &amp;&amp; p-&gt;value())if(file &amp;&amp; file.open()) 等),这不会影响正确性,只会影响效率。否则要小心!其次,还提到常量/表达式的混合评估会出错。这有一个简单(但冗长)的解决方法:只需使用 std::integral_constant(或 boost::mpl::int_)作为包装器。

    【讨论】:

    • 一个非常重载逻辑运算符的大问题是你失去了非常重要的短路属性,你根本没有提到。
    • @Xeo 我没有提到这一点,因为它 a) 如此明显,b) 因为我认为 OP 愿意付出这个代价。尽管如此,在例如一个用于符号逻辑计算的表达式模板库,出现指针错误的机会非常小,你同意吗?
    • @Xeo 无论如何,我用你的评论更新了答案。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 2023-03-26
    • 2019-06-21
    • 2016-11-12
    • 1970-01-01
    • 2021-01-20
    • 2011-11-05
    • 2015-07-04
    • 1970-01-01
    相关资源
    最近更新 更多