Проверить наличие нескольких значений при использовании операторов сравнения
Мне всегда казалось, что для любого оператора сравнения, т.е. X == Y
или X != Y
, это формат, и вы связываете утверждения вместе с &&
или ||
.
Нет ли способа написать X == (Y || Z)
вместо X == Y || X == Z
?
Изменить. Поскольку установлено, что это невозможно сделать чисто, как еще это можно сделать?
Ответы
Ответ 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;
}
доказательство компиляции. Xeo также отмечает, что std::any_of
, std::all_of
и std::none_of
могут быть полезны, в зависимости от ваших реальных потребностей и желаний.
Ответ 2
Нет чистого способа сделать то, что вы просите в С++.
Что вызывает много людей, так это то, что X == (Y || Z)
может быть юридическим выражением, и компилятор не будет жаловаться. Это будет просто ошибка.
Каждый оператор С++ должен самостоятельно оценивать значение true/false, а операторы просто связывают их вместе. То, что вы предлагаете, потребует некоторой внутренней структуры списка. На многих языках это (например, Python), но С++ - нет.
Ответ 3
С перегрузкой оператора вы можете получить точный синтаксис, который вы хотите. Но, как указывает Адам, это может привести к исключению действительных выражений.
Ниже приведен шаблон с перегрузкой оператора, функцией шаблона и макросом, чтобы получить синтаксис, аналогичный решению Mooing Duck nicer, но без требования С++ 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
Возможно, существует способ добиться того, чего вы хотите с шаблонами выражений. Ниже эскиза о том, как подойти к этому (не компилируется, много деталей отсутствует, caveat lector). Сначала вы настраиваете шаблон класса для представления логических значений и определяете некоторые операторы над ними.
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()); }
}
Очевидно, что для получения естественного синтаксиса С++ для того, что вы хотите, существует множество тяжелых шаблонов. Но с большим трудом и чтением вы, возможно, в конце концов получите что-то хорошее. (Вам нужно будет определить Atomic, OpAnd, OpOr, представления установки, содержащие первую и вторую ветки подвыражения и т.д. И т.д.)
Однако, даже если вы добьетесь успеха, вы получите действительно странную семантику в своей схеме. То, что вы предлагаете, требует ==
быть left-distributive над ||
или &&
. То есть для синтаксического анализа
X == (Y @OP Z)
как
(X == Y) @OP (X == Z)
с @OP
равным &&
или ||
. Я думаю, было бы естественно потребовать, чтобы ==
оставался симметричным. Это потребует также наложения права-дистрибутивности ==
на &&
и ||
. То есть для синтаксического анализа
(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))
Right-то левый
(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
совпадают, вы можете сгладить все дерево и переупорядочить термины, чтобы получить уникальный результат., он работает нормально, если вы используете те же операторы с обеих сторон ==
, потому что &&
и ||
являются ассоциативными, а также коммутативными.
Но для смешанных операторов полученные выражения, вообще говоря, будут разными.
UPDATE: как указано в комментариях к этому и другим ответам, вы также теряете определенные свойства встроенных типов. Во-первых, правила короткого замыкания, которые не выполняются перегруженными операторами. Для логических выражений, не связанных с разыменованием указателя или другим доступом к ресурсам (if(p && p->value())
или if(file && file.open())
и т.д.), Это не повлияло бы на правильность, а только на эффективность. В противном случае будьте осторожны! Во-вторых, было также упомянуто, что смешанные оценки констант/выражений будут ошибочными. У этого есть простое (но подробное) исправление: просто используйте std::integral_constant
(или boost::mpl::int_
) в качестве обертки.