429 votes

Comment vérifier qu'un élément est dans un std::set?

Comment vérifiez-vous qu'un élément est dans un ensemble?

Y a-t-il un équivalent plus simple du code suivant:

myset.find(x) != myset.end()

5 votes

La seule façon de simplifier davantage serait un prédicat booléen : template bool membre(T const &élément). Et cela serait implémenté (sous-jacent) en termes de la ligne sur laquelle vous posez des questions.

520voto

unwind Points 181987

À partir de C++20, vous pouvez utiliser contains pour vérifier l'existence dans de nombreux conteneurs STL tels que std::map, std::set, ... :

const bool is_in = container.contains(element);

Avant C++20, la façon typique était :

const bool is_in = container.find(element) != container.end();

29 votes

C'est spécifique pour les ensembles et les cartes. les vecteurs, listes, etc. n'ont pas de fonction membre find.

1 votes

@wilhelmtell Oui, vous avez raison. Parce que chercher un élément dans un vecteur ou une liste est inefficace en O(N), il n'y a pas de fonction membre find() implémentée dans un vecteur ou une liste.

16 votes

J'utilise IMO count() est meilleur car il est simplement plus court et il se convertit en bool comme indiqué dans la réponse de Pieter. Je ne comprends pas pourquoi cette réponse a été acceptée et a reçu autant de points...

296voto

Pieter Points 9200

Une autre façon de simplement vérifier si un élément existe est de vérifier le count()

if (myset.count(x)) {
   // x est dans l'ensemble, le compte est de 1
} else {
   // compte nul, c'est-à-dire x n'est pas dans l'ensemble
}

La plupart du temps, cependant, je me retrouve à avoir besoin d'accéder à l'élément partout où je vérifie son existence.

Donc je devrais trouver l'itérateur de toute façon. Ensuite, bien sûr, il vaut mieux simplement le comparer à end aussi.

set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
   // faire quelque chose avec *it
}

C++ 20

En C++20, set obtient une fonction contains, donc ce qui suit devient possible comme mentionné à : https://stackoverflow.com/a/54197839/895245

if (myset.contains(x)) {
  // x est dans l'ensemble
} else {
  // pas de x 
}

125 votes

Notez que l'utilisation de count() au lieu de find() n'est jamais meilleure mais potentiellement pire. Cela est dû au fait que find() renverra après le premier match, count() itérera toujours sur tous les éléments.

41 votes

@Frerich ce n'est pertinent que pour les multiset et les multimap je pensais? Toujours bon de le souligner :)

91 votes

Est généralement implémenté avec une structure d'arbre ordonné, donc count () et find () auront tous les deux O (log n). Aucun ne parcourra tous les éléments de l'ensemble.

52voto

Adhemar Points 415

Juste pour clarifier, la raison pour laquelle il n'y a pas de membre comme contains() dans ces types de conteneurs est parce que cela ouvrirait la porte à l'écriture de code inefficace. Une telle méthode ferait probablement simplement un this->find(key) != this->end() en interne, mais réfléchissez à ce que vous faites lorsque la clé est effectivement présente ; dans la plupart des cas, vous voudrez alors obtenir l'élément et faire quelque chose avec. Cela signifie que vous devriez faire un second find(), ce qui est inefficace. Il vaut mieux utiliser directement find, afin de pouvoir mettre en cache votre résultat, de la manière suivante :

auto it = myContainer.find(key);
if (it != myContainer.end())
{
    // Faire quelque chose avec, plus besoin de recherche.
}
else
{
    // La clé n'était pas présente.
}

Évidemment, si vous ne vous souciez pas de l'efficacité, vous pouvez toujours créer votre propre méthode, mais dans ce cas, vous ne devriez probablement pas utiliser C++... ;)

55 votes

Que dire des ensembles? En général, vous avez déjà l'élément, mais vous voulez juste vérifier s'il est présent.

9 votes

Avez-vous des références indiquant si c'est effectivement la raison pour laquelle une méthode/fonction n'est pas incluse dans la STL, ou est-ce simplement votre hypothèse éclairée?

3 votes

@FabioA. C'est mon estimation éduquée.

9voto

280Z28 Points 49515

Si vous deviez ajouter une fonction contains, elle pourrait ressembler à ceci :

#include 
#include 

template inline
bool contains(TInputIterator first, TInputIterator last, const T& value)
{
    return std::find(first, last, value) != last;
}

template inline
bool contains(const TContainer& container, const T& value)
{
    // Cela fonctionne avec plus de conteneurs mais nécessite std::begin et std::end
    // de C++0x, que vous pouvez obtenir de deux manières :
    //  1. En utilisant un compilateur C++0x ou
    //  2. En incluant les fonctions utilitaires ci-dessous.
    return contains(std::begin(container), std::end(container), value);

    // Cela fonctionne avant C++0x (et sans les fonctions utilitaires ci-dessous, mais ne fonctionne pas
    // pour les tableaux de longueur fixe.
    //return contains(container.begin(), container.end(), value);
}

template inline
bool contains(const std::set& container, const T& value)
{
    return container.find(value) != container.end();
}

Ceci fonctionne avec std::set, d'autres conteneurs STL, et même des tableaux de longueur fixe :

void test()
{
    std::set set;
    set.insert(1);
    set.insert(4);
    assert(!contains(set, 3));

    int set2[] = { 1, 2, 3 };
    assert(contains(set2, 3));
}

Edit:

Comme indiqué dans les commentaires, j'ai utilisé involontairement une fonction nouvelle à C++0x (std::begin et std::end). Voici l'implémentation presque triviale de VS2010 :

namespace std {

template inline
    typename _Container::iterator begin(_Container& _Cont)
    { // obtenir le début de la séquence
    return (_Cont.begin());
    }

template inline
    typename _Container::const_iterator begin(const _Container& _Cont)
    { // obtenir le début de la séquence
    return (_Cont.begin());
    }

template inline
    typename _Container::iterator end(_Container& _Cont)
    { // obtenir la fin de la séquence
    return (_Cont.end());
    }

template inline
    typename _Container::const_iterator end(const _Container& _Cont)
    { // obtenir la fin de la séquence
    return (_Cont.end());
    }

template inline
    _Ty *begin(_Ty (&_Array)[_Size])
    { // obtenir le début du tableau
    return (&_Array[0]);
    }

template inline
    _Ty *end(_Ty (&_Array)[_Size])
    { // obtenir la fin du tableau
    return (&_Array[0] + _Size);
    }

}

1 votes

@Adhemar, c'était en fait inefficace, mais pas du tout pour la raison que vous avez mentionnée.

0 votes

@Paul: Assurez-vous d'inclure la spécialisation pour std::set, et n'oubliez pas que c'est uniquement approprié si la seule chose que vous devez savoir est son existence.

0 votes

@280Z28: std::begin(container)? Quelle norme STL est-ce ? Cela ne se compile pas sur mon gcc.

0voto

stefaanv Points 7326

Écrivez le vôtre :

template
bool checkElementIsInSet(const T& elem, const std::set& container)
{
  return container.find(elem) != container.end();
}

4 votes

Juste faites le : modèle statique en ligne contient(const std::set& S, T x) { retourner (S.find(x) != S.end()); }

4 votes

@paul ne créez pas de fonctions globales statiques. Placez plutôt votre fonction dans un espace de noms anonyme : c'est la manière de créer des fonctions en C++ qui ne se lieront pas à d'autres unités de compilation. De plus, votre paramètre T devrait être une référence constante, pour la correction constante et pour l'efficacité.

0 votes

-1: Pas modelé et pas du tout au style STL. C'est correct si vous n'utilisez pas STL, mais si vous l'utilisez, vous devriez au moins essayer de suivre ses normes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X