STL-Container mit einem bestimmten Typ als generisches Argument


25

Gibt es eine Möglichkeit, eine Funktion zu erstellen, die einen Container mit einem bestimmten Typ (z. B. std::string) als Parameter verwendet?

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

und es für jede Art von stl-Container als Eingabe aufrufen? wie oben?

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

foo(strset);
foo(strvec);
foo(strlist);

2
Ja, es heißt Vorlagenfunktion. ;)
Ulrich Eckhardt

2
Es wird oft als besser angesehen, ein Paar Iteratoren zu übergeben (die den Anfang bzw. das Ende des Containers darstellen). Solange Iteratoren die Anforderungen der Funktion erfüllen, spielt es keine Rolle (oft gibt es einige Ausnahmen), aus welcher Art von Containern sie stammen.
Peter

Antworten:


21

Sie können fooeine Funktionsvorlage erstellen, die einen Vorlagenvorlagenparameter für den Containertyp verwendet.

z.B

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

LEBEN


Ich denke, wir können es noch weiter verallgemeinern. Siehe meine Antwort.
theWiseBro

Lars ' Antwort ist besser, da sie auch mit Arrays im C-Stil funktioniert.
Ayxan

1
@theWiseBro Ja, das ist im Allgemeinen eine gute Idee. Aber ich denke, OP möchte es nur mit einem bestimmten Typ verwenden std::string, also ...
songyuanyao

3
@theWiseBro genau. OP sagte, dass es mit einem bestimmten Typ funktionieren sollte . Daher gibt es keinen Vorteil, es weiter zu verallgemeinern.
M. Spiller

1
@theWiseBro Ich verstehe, was du meintest. Ich bin mir nicht sicher über die ursprüngliche Absicht von OP, er sagte nur, ich möchte einen bestimmten Typ; Möglicherweise müssen Sie es OP erklären. :)
songyuanyao

6

Abhängig davon, ob Sie foofür andere Fälle überladen möchten oder nicht

// 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;
   }
}

Sie können einen anderen Test verwenden std::is_same, std::is_convertibleum dies zuzulassen

std::vector<char *> c_strings;
foo(c_strings);

0

Möglicherweise möchten Sie stattdessen Iteratoren verwenden. Ein Zwischenergebnis kann so aussehen

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';
  }
}

Verwenden Sie jetzt eine aufrufbare Vorlage:

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

Wir haben gerade gelernt, das zu nutzen, was die STL bereits bietet.


-1

Ich denke, wir können die Antwort von @ songyuanyao weiter verallgemeinern, um:

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

1
Dadurch wird der Elementtyp nicht auf std :: string beschränkt, sodass die Frage nicht beantwortet wird.
Sasha

@ Sasha Es ist wahr, dass dies nicht auf std :: string festgelegt ist, aber es ist allgemeiner. Das OP möchte einen bestimmten Typ verwenden. Angenommen, er verwendet heute std :: string und möchte morgen stattdessen einen MyCustomString verwenden. Wäre dies nicht einfacher zu warten, da er den Code nur an einer einzigen Stelle bearbeiten muss?
theWiseBro

Aber das zeigt nicht , wie es zu beschränken auf beide std :: string oder MyCustomString Elemente - und das querent speziell nehmen „einen Container wollte mit einem bestimmten Typ “. So wie es ist, wird jeder Typ akzeptiert, der zufällig eine Vorlage ist, und an diesem Punkt, warum nicht einfach stattdessen eine Vorlage auf einem einzelnen <Typnamen C> erstellen? Das ist viel einfacher und etwas allgemeiner - z. B. wird bei Ihnen ein std :: string (auch bekannt als std :: basic_string <char>) als Container verwendet, jedoch keine benutzerdefinierte Struktur MyCustomString, sodass diese nicht vollständig generisch ist.
Sasha

Wenn die Funktion erwartet, dass die Elemente std :: string sind, wird die Verwendung und Wartung durch Benutzer schwieriger , wenn sie ein std :: tuple <int, double, std :: nullptr_t> übergeben können .
Sasha

@ Sasha hmm. Ich weiß, worauf du hinauswillst. Das stimmt. Danke für die Warnung!
theWiseBro
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.