La voie de la facilité
L'approche la plus simple est d'écrire une fonction membre wrapper appelés in()
autour std::find
avec une paire d'itérateurs pour chercher les données en question. J'ai écrit un simple template<class It> in(It first, It last)
fonction de membre pour que
template<class It>
bool in(It first, It last) const
{
return std::find(first, last, data) != last;
}
Si vous n'avez pas accès à la source de foo
, vous pouvez écrire un non-fonctions de membre de la signature template<class T> bool in(foo const&, std::initializer_list<T>)
etc., et de l'appeler comme les
in(f, {1, 2, 3 });
La dure
Mais nous laisser complètement aller à la mer avec ça: il suffit d'ajouter deux de plus public
surcharges:
- celui qui prend un
std::initializer_list
paramètre que l'on appelle la précédente, avec l' begin()
et end()
des itérateurs de la correspondante de la liste d'initialiseur argument.
- l'un pour l'arbitraire d'un conteneur d'entrée qui permettra de faire un peu de balise d'envoi de deux plus
private
surcharges d'une detail_in()
helper:
- une surcharge de faire un SFINAE truc avec fuite de type de retour
decltype(c.find(data), bool())
qui sera retiré de la surcharge de définir si le conteneur c
en question n'a pas une fonction membre find()
, et que les retours bool
sinon (ce résultat est obtenu par l'abus de l' opérateur virgule à l'intérieur d' decltype
)
- un repli de surcharge qui prend simplement l'
begin()
et end()
itérateurs et les délégués à l'origine in()
la prise de deux itérateurs
Parce que les étiquettes pour l' detail_in()
helper forme d'une hiérarchie d'héritage (un peu comme le standard de l'itérateur de balises), la première surcharge de match pour les conteneurs associatifs std::set
et std::unordered_set
et leur multi-cousins. Tous les autres conteneurs, y compris C-tableaux, std::array
, std::vector
et std::list
, va correspondre à la deuxième surcharge.
#include <algorithm>
#include <array>
#include <initializer_list>
#include <type_traits>
#include <iostream>
#include <set>
#include <unordered_set>
#include <vector>
class foo
{
public:
int data;
template<class It>
bool in(It first, It last) const
{
std::cout << "iterator overload: ";
return std::find(first, last, data) != last;
}
template<class T>
bool in(std::initializer_list<T> il) const
{
std::cout << "initializer_list overload: ";
return in(begin(il), end(il));
}
template<class Container>
bool in(Container const& c) const
{
std::cout << "container overload: ";
return detail_in(c, associative_container_tag{});
}
private:
struct sequence_container_tag {};
struct associative_container_tag: sequence_container_tag {};
template<class AssociativeContainer>
auto detail_in(AssociativeContainer const& c, associative_container_tag) const
-> decltype(c.find(data), bool())
{
std::cout << "associative overload: ";
return c.find(data) != end(c);
}
template<class SequenceContainer>
bool detail_in(SequenceContainer const& c, sequence_container_tag) const
{
std::cout << "sequence overload: ";
using std::begin; using std::end;
return in(begin(c), end(c));
}
};
int main()
{
foo f{1};
int a1[] = { 1, 2, 3};
int a2[] = { 2, 3, 4};
std::cout << f.in({1, 2, 3}) << "\n";
std::cout << f.in({2, 3, 4}) << "\n";
std::cout << f.in(std::begin(a1), std::end(a1)) << "\n";
std::cout << f.in(std::begin(a2), std::end(a2)) << "\n";
std::cout << f.in(a1) << "\n";
std::cout << f.in(a2) << "\n";
std::cout << f.in(std::array<int, 3>{ 1, 2, 3 }) << "\n";
std::cout << f.in(std::array<int, 3>{ 2, 3, 4 }) << "\n";
std::cout << f.in(std::vector<int>{ 1, 2, 3 }) << "\n";
std::cout << f.in(std::vector<int>{ 2, 3, 4 }) << "\n";
std::cout << f.in(std::set<int>{ 1, 2, 3 }) << "\n";
std::cout << f.in(std::set<int>{ 2, 3, 4 }) << "\n";
std::cout << f.in(std::unordered_set<int>{ 1, 2, 3 }) << "\n";
std::cout << f.in(std::unordered_set<int>{ 2, 3, 4 }) << "\n";
}
Exemple vivant que -pour tous les types de conteneurs- tirages de 1 et de 0 pour les deux séries.
Les cas d'utilisation pour l' std::initializer_list
de surcharge sont pour les états-navire de test pour de petits ensembles de nombres que vous écrivez explicitement dans le code appelant. Il a O(N)
de complexité, mais permet d'éviter toute allocations de tas.
Pour quelque chose de lourd, comme l'appartenance des tests de grands ensembles, vous pouvez stocker les numéros dans un conteneur associatif comme std::set
, ou de ses multi_set
ou unordered_set
des cousins. Cela permettra d'aller au tas, lors du stockage de ces chiffres, mais a O(log N)
ou même O(1)
recherche de complexité.
Mais si vous avez juste un conteneur de séquence pleine de chiffres, vous pouvez également jeter que de la classe et il se fera un plaisir de calcul d'adhésion pour vous en O(N)
du temps.