web-dev-qa-db-fra.com

C ++ Le moyen le plus efficace pour comparer une variable à plusieurs valeurs?

À quelques reprises dans mon programme, j'ai dû vérifier si une variable était l'une des nombreuses options. Par exemple

if (num = (<1 or 2 or 3>)) { DO STUFF }

J'ai joué avec les "OU", mais rien ne semble aller. J'ai essayé

if (num == (1 || 2 || 3))

mais ça ne fait rien. Aidez-moi! Merci d'avance.

P.S. J'ai besoin de distinguer plusieurs groupes. Par exemple...

if (num = (1,2,3))

else if (num = (4,5,6))

else if (num = (7,8,9))
15
Matt Reynolds

Si les valeurs que vous souhaitez vérifier sont suffisamment petites, vous pouvez créer un masque de bits des valeurs que vous recherchez, puis vérifier que ce bit est défini.

Supposons que vous vous souciez de quelques groupes.

static const unsigned values_group_1 = (1 << 1) | (1 << 2) | (1 << 3);
static const unsigned values_group_2 = (1 << 4) | (1 << 5) | (1 << 6);
static const unsigned values_group_3 = (1 << 7) | (1 << 8) | (1 << 9);    
if ((1 << value_to_check) & values_group_1) {
  // You found a match for group 1
}
if ((1 << value_to_check) & values_group_2) {
  // You found a match for group 2
}
if ((1 << value_to_check) & values_group_3) {
  // You found a match for group 3
}

Cette approche fonctionne mieux pour les valeurs qui ne dépassent pas la taille naturelle avec laquelle votre processeur aime travailler. Ce serait généralement 64 dans les temps modernes, mais peut varier en fonction des spécificités de votre environnement.

11
Eric Johnson

Voici un moyen en C++ 11, en utilisant std::initializer_list:

#include <algorithm>
#include <initializer_list>

template <typename T>
bool is_in(const T& v, std::initializer_list<T> lst)
{
    return std::find(std::begin(lst), std::end(lst), v) != std::end(lst);
}

avec ça, vous pouvez faire:

if (is_in(num, {1, 2, 3})) { DO STUFF }

Il n'est cependant pas très efficace lorsqu'il n'est pas utilisé avec des types intégrés. int fonctionnera bien, mais si vous comparez std::string variables par exemple, le code produit est tout simplement horrible.

En C++ 17 cependant, vous pouvez utiliser à la place une solution beaucoup plus efficace qui fonctionne bien avec n'importe quel type:

template<typename First, typename ... T>
bool is_in(First &&first, T && ... t)
{
    return ((first == t) || ...);
}

// ...

// s1, s2, s3, s4 are strings.
if (is_in(s1, s2, s3, s4)) // ...

La version C++ 11 serait très inefficace ici, alors que cette version devrait produire le même code que les comparaisons manuscrites.

30
Nikos C.

Je viens d'avoir un problème similaire et je suis arrivé à ces solutions C++ 11:

template <class T> 
struct Is 
{ 
  T d_; 
  bool in(T a) { 
    return a == d_; 
  } 
  template <class Arg, class... Args> 
  bool in(Arg a, Args... args) { 
    return in(a) || in(args...); 
  } 
}; 

template <class T> 
Is<T> is(T d) { 
  return Is<T>{d}; 
}

Ou comme alternative sans la méthode de terminaison de récursivité. Sachez qu'ici, l'ordre des comparaisons n'est pas défini et que cela ne se termine pas tôt si la première correspondance est trouvée. Mais le code est plus compact.

template <class T>
struct Is {
  const T d_;
  template <class... Args>
  bool in(Args... args) {
    bool r{ false }; 
    [&r](...){}(( (r = r || d_ == args), 1)...);
    return r;
  }
};

template <class T>
Is<T> is(T d) { 
  return Is<T>{d}; 
}

Ainsi, pour les deux solutions, le code ressemblerait à:

if (is(num).in(1,2,3)) {
  // do whatever needs to be done
}
6
Felix Petriconi

Vous devez faire la comparaison avec chaque valeur. Par exemple.

if (num == 1 || num == 2 || num == 3) { stuff }

Vous voudrez peut-être également envisager un changement et une erreur intentionnelle (bien que je ne pense pas que ce soit la meilleure solution pour ce que vous déclarez).

switch (num) {
    case 1:
    case 2:
    case 3:
        {DO STUFF}
        break;

    default:
        //do nothing.
}
5
john.pavan

Vous pouvez définir un ensemble d'entiers, y ajouter les valeurs souhaitées, puis utiliser la méthode find pour voir si la valeur en question se trouve dans l'ensemble

std::set<int> values;
// add the desired values to your set...
if (values.find(target) != values.end())
    ...
3
dasblinkenlight

J'avais besoin de faire quelque chose de similaire pour les énumérations. J'ai une variable et je souhaite la tester par rapport à une plage de valeurs.

Ici, j'ai utilisé une fonction de modèle variadic. Notez la spécialisation pour le type const char*, De sorte que is_in( my_str, "a", "b", "c") a le résultat attendu lorsque my_str Stocke "a".

#include <cstring> 

template<typename T>
constexpr  bool is_in(T t, T v) {
  return t == v;
}

template<>
constexpr  bool is_in(const char* t, const char* v) {
  return std::strcmp(t,v);
}

template<typename T, typename... Args>
constexpr bool is_in(T t, T v, Args... args) {
  return  t==v || is_in(t,args...);
}

Exemple d'utilisation:

enum class day
{
  mon, tues, wed, thur, fri, sat, Sun
};

bool is_weekend(day d)
{
  return is_in(d, day::sat, day::Sun);
}
2
Darren Smith