Подтвердить что ты не робот

Имеет ли С++ оператор сравнения, который задает диапазон значений? (например, "in" на языке E)?

Мне нужно написать условие, которое проверяет, является ли переменная enum в диапазоне значений, например, это может быть сделано на языке E:

enum EnumVariable {a, b, d, g, f, t, k, i};
if (EnumVariable in [ a, g, t, i]) {
    ...
}

Есть ли лучший способ в С++, чем спросить 4 раза if EnumVariable==a or EnumVariable==b и т.д.?

4b9b3361

Ответ 1

Оказалось, что нескольким людям понравился мой комментарий и попросил опубликовать его в качестве ответа, поэтому:

Фактически вы можете использовать switch для целых типов без break -s между некоторыми значениями.

Например, ваш код может выглядеть так:

enum EnumVariable {a, b, d, g, f, t, k, i};
switch( EnumVariable )
{
case a:
case g:
case t:
case i:
    // do something
    break;
// handler other values
};

Это будет очень эффективно, но будет работать только для интегральных типов, а не для std::string, например. Для других типов вы можете попытаться использовать некоторые из предложений других ответов.

Это немного для написания, но очень, очень эффективно и делает то, что вам нужно.

Ответ 2

В С++ 11 краткий способ выразить это с помощью синтаксиса brace-init для построения набора и вызова std::set::count для проверки присутствия элемента:

if (std::set<EnumVariable>{a, g, t, i}.count(myVariable))
  ...

Компилируемый пример:

#include <set>

enum EnumVariable {a, b, d, g, f, t, k, i};

int main()
{
  EnumVariable x = t;
  if (std::set<EnumVariable>{a, g, t, i}.count(x)) {
    return 0;
  }
  return 1;
}

Обратите внимание на эффективность: поскольку вопрос задан о "операторе сравнения", ответ написан с целью предоставления достаточно читаемого и краткого одиночного выражения, которое не требует отдельной функции полезности. Поскольку он создает временный std::set, он не так эффективен, как оператор switch, который скомпилирован в тесты или таблицы переходов всеми соответствующими компиляторами.

Ответ 3

Существует не тот же компактный синтаксис, но вы можете использовать std::find с std::initializer_list. Например,

#include <algorithm>
#include <iostream>

enum EnumVariable {a, b, d, g, f, t, k, i};

int main()
{
  EnumVariable e = k;
  auto vals = { a, g, t, i }; // std::initializer_list<EnumVariable>
  std::cout << std::boolalpha;
  std::cout << (std::find(vals.begin(), vals.end(), e) != vals.end()) << std::endl;
}

Это может быть легко обернуто в вспомогательную функцию, которая принимает значение перечисления, список и возвращает bool. Например:

template <typename T>
bool is_in(T elem, std::initializer_list<T> range)
{
 for (T i : range)
    if (i == elem) return true;
 return false;

}

использование:

int main()
{
  EnumVariable ev = a;
  std::cout << std::boolalpha;
  std::cout << is_in(ev, {a, g, t, i}) << std::endl;
}

Ответ 4

это часто/обычно выполняется с использованием битовых полей в перечислении (очевидно, до количества бит интегрального типа (обычно 32 бита), который является типом по умолчанию в базе перечисления):

#include <iostream>

enum EnumVariable {
  a=0x0001, 
  b=0x0002, 
  d=0x0004, 
  g=0x0008, 
  f=0x0010, 
  t=0x0020, 
  k=0x0040, 
  i=0x0080
};

inline bool CheckMatch(EnumVariable var, EnumVariable mask) {
  return (var & mask) != 0;
}

using namespace std;

int main(int argc, char **argv) {
  EnumVariable mask = EnumVariable(a|g|t|i);
  EnumVariable x1 = b;
  EnumVariable x2 = g;

  cout << "x1: " << (CheckMatch(x1, mask) ? "match" : "no-match") << endl;
  cout << "x2: " << (CheckMatch(x2, mask) ? "match" : "no-match") << endl;

}

что в ближайшее время можно увидеть так:

if ( x1 & (a|g|t|i) ) {
  ...
}

Ответ 5

Тем не менее, другой подход заключается в использовании шаблона для указания списка для соответствия, который для вашего случая enum предположительно будет известен во время компиляции (в противном случае подход switch неприменим). Это позволяет использовать следующие обозначения:

if (in<a, b, c, d, e, f>(x))
    ...

При включенной оптимизации это должно быть включено и получить что-то эквивалентное:

a == x || b == x || ...

Реализация ниже (также ideone). Я не играл с вариационными шаблонами, поэтому предложения приветствуются. В частности, как сделать это безопасным для типа, поэтому заданный enum E { E1 }; enum F { F1 };, in<E1>(F1) не будет компилироваться (не ограничивая его конкретным типом enum).

#include <iostream>
using namespace std;

template <int First, int... Numbers>
inline bool in_impl(int n)
{
    return n == First || in_impl<Numbers...>(n);
}

template <>
inline bool in_impl<int(-1)>(int n)
{
    return false;
}

template <int... Numbers>
inline bool in(int n) 
{
    return in_impl<Numbers..., int(-1)>(n);
}

enum E { E1, E2, E3, E4 };

int main()
{
    std::cout << in<3, 5, 7, 9>(4) << '\n';
    std::cout << in<3, 5, 7, 9>(5) << '\n';
    std::cout << in<E2, E4>(E1) << '\n';
    std::cout << in<E2, E4>(E2) << '\n';
}

Вывод:

0
1
0
1

Ответ 6

Это абсолютно не идиоматично и, вероятно, следует избегать, но ближе всего вы можете (синтаксически говоря) использовать названные операторы. Именованные операторы фактически соответствуют злоупотреблению операционными перегрузками.

enum EnumVariable {a, b, d, g, f, t, k, i};

auto in = make_named_operator(
[](int i, std::initializer_list<EnumVariable> const& x) {
    return std::find(std::begin(x), std::end(x), i) != std::end(x);
});

auto vals = { a, g, t, i };
if (g <in> vals)
{
    std::cout << "It works!" << std::endl;
}

Здесь - рабочий пример на Coliru.


Если вы используете С++ 14 и полиморфные лямбда, вы можете сделать приведенный выше пример более общим:

auto in = make_named_operator(
[](int i, auto const& x) {
    return std::find(std::begin(x), std::end(x), i) != std::end(x);
});