вспомогательная функция для проверки значения любого из ее параметров - PullRequest
2 голосов
/ 06 февраля 2020

Мне нужно проверить, является ли значение типа T любым из его параметров, используя вспомогательную функцию.

Например, используя что-то вроде следующего кода

enum class my_enum{k1, k2, k3, k4, k5};
auto v{my_enum::k1};
if (is_any_of(v, my_enum::k1, my_enum::k4, my_enum::k5)) {
}

вместо использования if (v == my_enum::k1 || v == my_enum::k4 || v== my_enum::k5) {} или с использованием switch-case.

Как реализовать функцию c variadi bool is_any_of() в C ++ 11?

Как реализация станет проще с C ++ 17 кратных выражений?

Ответы [ 3 ]

3 голосов
/ 06 февраля 2020

Это будет работать в C ++ 11 и будет делать то, что вы хотите, до тех пор, пока все типы будут сравниваться друг с другом.

template<typename T, typename R>
bool is_any_of(T t, R r)
{
   return t == r;
}

template<typename T, typename R, typename... ARGS>
bool is_any_of(T t, R r, ARGS... args)
{
   if (t == r)
   {
      return true;
   }
   else
   {
      return is_any_of(t, args...);
   }
}

Это гораздо более компактно и будет работать в C ++ 17

template<typename T, typename... ARGS>
bool is_any_of(T t, ARGS... args)
{
   return ((t == args) || ...);
}
2 голосов
/ 06 февраля 2020

Для нерекурсивной альтернативы C ++ 11:

template <typename T, typename... Ts>
bool is_any_of (T t, Ts&&... ts) {
    std::initializer_list<bool> eq { (t == ts)... };
    return std::any_of(eq.begin(), eq.end(), [](bool i){ return i; });
}

https://godbolt.org/z/K7xtia

Но, как уже отвечали, выражение сгиба return ((t == ts) || ...); более компактный, выразительный и оптимизируемый.

0 голосов
/ 06 февраля 2020

Вы уже получили хорошие решения на основе шаблонов. Я сам написал один, но он дублировал другой ответ. Таким образом, этот ответ отличается, но он также не использует шаблоны. Вместо этого он использует макрос variadi c.

Если вам нужно истинное поведение при коротком замыкании, вам нужно будет развернуть полное выражение. В противном случае, если параметры имеют побочные эффекты, передача их в функцию вызовет их все. В качестве надуманного примера,

template <typename T>
const T & x(const T &v) {
    std::cout << __func__ << ": " << v << '\n';
    return v;
}

  //...
  if (is_any_of(2, x(1), x(2), x(3))) {
    //...
  }

Реализация короткого замыкания позволит избежать вызова x(3) при обнаружении совпадения на x(2). Но для этого нужно, чтобы is_any_if(...) расширилось до:

  if ((2 == x(1)) || (2 == x(2)) || (2 == x(3))) {
    //...

Вы можете использовать макрос для выполнения sh этого расширения. Ниже приведена одна из возможных реализаций, которая может принимать до 9 параметров для проверки.

#define is_any_of(...) \
        (V_(V_(is_any_of_X(__VA_ARGS__,A,9,8,7,6,5,4,3,2,_))(__VA_ARGS__)))

#define is_any_of_X(_A,_9,_8,_7,_6,_5,_4,_3,_2,_1,X,...) is_any_of_##X

#define is_any_of_A(V, X, ...) ((X) == (V)) || is_any_of_9(V, __VA_ARGS__)
#define is_any_of_9(V, X, ...) ((X) == (V)) || is_any_of_8(V, __VA_ARGS__)
#define is_any_of_8(V, X, ...) ((X) == (V)) || is_any_of_7(V, __VA_ARGS__)
#define is_any_of_7(V, X, ...) ((X) == (V)) || is_any_of_6(V, __VA_ARGS__)
#define is_any_of_6(V, X, ...) ((X) == (V)) || is_any_of_5(V, __VA_ARGS__)
#define is_any_of_5(V, X, ...) ((X) == (V)) || is_any_of_4(V, __VA_ARGS__)
#define is_any_of_4(V, X, ...) ((X) == (V)) || is_any_of_3(V, __VA_ARGS__)
#define is_any_of_3(V, X, ...) ((X) == (V)) || is_any_of_2(V, __VA_ARGS__)
#define is_any_of_2(V, X)      ((X) == (V))
#define is_any_of_1(...)       false
#define is_any_of_0(...)       true

#define is_any_of_Y(_1,Y,...) is_any_of_##Y
#define is_any_of__(...) I_(is_any_of_Y, E_ __VA_ARGS__ () 0, 1)(__VA_ARGS__)

#define V_(...) __VA_ARGS__
#define I_(M,...) V_(M(__VA_ARGS__))
#define E_() _,

Методика объяснена здесь . Вкратце, он применяет трюк подсчета аргументов макроса, а также переносимый метод для определения, является ли регистр с одним аргументом регистром пустого аргумента. (Если вы используете G CC, обнаружение может быть упрощено.)

...