Conteneur STL avec un type spécifique comme argument générique

25

Existe-t-il un moyen de créer une fonction qui prend un conteneur avec un type spécifique (disons std::string) comme paramètre

void foo(const std::container<std::string> &cont)
{
   for(std::string val: cont) {
      std::cout << val << std::endl;
   }
}

et l'appeler pour chaque type de conteneur stl en entrée? comme ci-dessus?

std::set<std::string> strset;
std::vector<std::string> strvec;
std::list<std::string> strlist;

foo(strset);
foo(strvec);
foo(strlist);
chatzich
la source
2
Oui, cela s'appelle une fonction de modèle. ;)
Ulrich Eckhardt
2
Il est souvent considéré comme préférable de passer une paire d'itérateurs (représentant respectivement le début et la fin du conteneur). Tant que les itérateurs répondent aux exigences de la fonction, il (souvent, il y a quelques exceptions) n'a pas d'importance de quel type de conteneurs ils ont été obtenus.
Peter

Réponses:

21

Vous pouvez créer fooun modèle de fonction en prenant un paramètre de modèle de modèle pour le type de conteneur.

par exemple

template<template<typename...> typename C>
void foo(const C<std::string> &cont)
{
   for(std::string val: cont) {
      std::cout << val << std::endl;
   }
}

VIVRE

songyuanyao
la source
Je pense que nous pouvons le généraliser encore plus. Voir ma réponse.
theWiseBro
La réponse de Lars est meilleure car elle fonctionne également avec les tableaux de style C.
Ayxan
1
@theWiseBro Oui, c'est une bonne idée en général. Mais je pense que OP veut juste l'utiliser avec un type spécifique comme std::string, alors ..
songyuanyao
3
@theWiseBro exactement. OP a déclaré qu'il devrait fonctionner avec un type spécifique . Il n'y a donc aucun avantage à le généraliser davantage.
M. Spiller
1
@theWiseBro Je comprends ce que tu voulais dire. Je ne suis pas sûr de l'intention originale de OP, il vient de dire vouloir un type spécifique; vous devrez peut-être l'expliquer à OP. :)
songyuanyao
6

Selon que vous souhaitez fooou non surcharger d'autres cas

// Doesn't participate in overload resolution when not applicable
template<typename Container, typename = std::enable_if_t<std::is_same_v<typename Container::value_type, std::string>>>
void foo(const Container &cont) {
   for(std::string val: cont) {
      std::cout << val << std::endl;
   }
}

// simpler
template<typename Container>
void foo(const Container &cont) {
   static_assert(std::is_same_v<typename Container::value_type, std::string>, "Container must contain std::string")
   for(std::string val: cont) {
      std::cout << val << std::endl;
   }
}

Vous pouvez utiliser un autre test pour std::is_same, par exemple, std::is_convertibleautoriser

std::vector<char *> c_strings;
foo(c_strings);
Caleth
la source
0

Vous voudrez peut-être envisager d'utiliser des itérateurs à la place. Un résultat intermédiaire peut ressembler à

template<typename Iter>
void foo(Iter begin, Iter end) {
  using T = decltype(*begin);
  std::for_each(begin, end, [] (cons T & t) {
    std::out << t << '\n';
  }
}

Maintenant, en utilisant un modèle appelable:

template<typename Iter, typename Callable>
void foo(Iter begin, Iter end, Callable & c) {
  std::for_each(begin, end, c);
}

Nous venons d'apprendre à utiliser ce que la STL propose déjà.

user1624886
la source
-1

Pour compléter la réponse de @ songyuanyao, je pense que nous pouvons la généraliser davantage:

template<template<typename...> typename C, typename ... D>
void foo(const C<D...> &cont)
{
   for(const auto& val: cont) {
      std::cout << val << std::endl;
   }
}
theWiseBro
la source
1
Cela ne limite pas le type d'élément à std :: string, donc il ne répond pas à la question.
Sasha
@Sasha C'est vrai que ce n'est pas fixé sur std :: string mais c'est plus généralisé. L'OP souhaite utiliser un type spécifique. Supposons aujourd'hui qu'il utilise std :: string et que demain il souhaite utiliser une chaîne MyCustomString à la place. Cela ne serait-il pas plus facile à maintenir car il n'a qu'à éditer le code en un seul endroit?
theWiseBro
Mais cela ne montre pas comment limiter à une ou l' autre std :: string ou éléments MyCustomString - et querent voulait spécifiquement prendre « un récipient avec un type spécifique ». En l'état, il acceptera tout type qui se trouve être un modèle, et à ce moment-là, pourquoi ne pas simplement le modèle sur un seul <typename C> à la place? C'est beaucoup plus simple et légèrement plus généralisé - par exemple, le vôtre prendra une chaîne std :: (alias std :: basic_string <char>) comme conteneur mais pas une structure personnalisée MyCustomString, il n'est donc pas entièrement générique.
Sasha
Et si la fonction s'attend à ce que les éléments soient std :: string, permettre aux utilisateurs de passer un std :: tuple <int, double, std :: nullptr_t> rend son utilisation et sa maintenance plus difficiles .
Sasha
@Sasha hmm. Je vois ce que tu veux dire. C'est vrai. Merci pour l'information!
theWiseBro