Contenitore STL con un tipo specifico come argomento generico


25

Esiste un modo in cui posso fare una funzione che accetta un contenitore con un tipo specifico (diciamo std::string) come parametro

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

e chiamarlo per ogni tipo di contenitore stl come input? come sopra?

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

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

2
Sì, si chiama una funzione modello. ;)
Ulrich Eckhardt il

2
È spesso considerato meglio passare una coppia di iteratori (che rappresentano rispettivamente l'inizio e la fine del contenitore). Finché gli iteratori soddisfano i requisiti della funzione, (spesso ci sono alcune eccezioni) non importa da quale tipo di contenitori sono stati ottenuti.
Peter,

Risposte:


21

È possibile creare fooun modello di funzione prendendo un parametro modello di modello per il tipo di contenitore.

per esempio

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

VIVERE


Penso che possiamo generalizzarlo ulteriormente. Vedi la mia risposta
theWiseBro

La risposta di Lars è migliore perché funziona anche con array in stile C.
Ayxan,

1
@theWiseBro Sì, è una buona idea in generale. Ma penso che OP voglia solo usarlo con un tipo specifico come std::string, quindi ...
Songyuanyao,

3
@theWiseBro esattamente. OP ha detto che dovrebbe funzionare con un tipo specifico . Pertanto non vi è alcun vantaggio a generalizzarlo ulteriormente.
M. Spiller,

1
@theWiseBro Capisco cosa intendevi. Non sono sicuro dell'intento originale di OP, ha appena detto di volere un tipo specifico; potrebbe essere necessario spiegarlo a OP. :)
Songyuanyao,

6

A seconda se si desidera sovraccaricare fooper altri casi o meno

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

È possibile utilizzare un test diverso da std::is_same, ad esempio std::is_convertibleper consentire

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

0

Si consiglia di utilizzare invece gli iteratori. Potrebbe apparire un risultato intermedio

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

Ora usando un modello richiamabile:

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

Abbiamo appena imparato a utilizzare ciò che la STL offre già.


-1

Aggiungendo alla risposta di @ songyuanyao, penso che possiamo generalizzare ulteriormente a:

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

1
Questo non limita il tipo di elemento a std :: string, quindi non risponde alla domanda.
Sasha,

@Sasha È vero che questo non è risolto in std :: string ma è più generalizzato. L'OP vuole utilizzare un tipo specifico. Diciamo che oggi sta usando std :: string e domani vuole invece usare un MyCustomString. Non sarebbe più facile da mantenere poiché deve solo modificare il codice in un unico posto?
theWiseBro

Ma questo non mostra come limitare a uno std :: string o elementi MyCustomString - e il consultante specificamente voluto prendere "un contenitore con un tipo specifico ". Così com'è, accetterà qualsiasi tipo che capiti di essere un modello, e a quel punto perché non modellarlo su un singolo <nome di battesimo C> invece? È molto più semplice e leggermente più generalizzato, ad esempio il tuo prenderà uno std :: string (aka std :: basic_string <char>) come contenitore ma non una struttura personalizzata MyCustomString, quindi non è del tutto generico.
Sasha,

E se la funzione prevede che gli elementi siano std :: string, consentendo agli utenti di passare uno std :: tuple <int, double, std :: nullptr_t> rende più difficile l'uso e la manutenzione.
Sasha,

@Sasha hmm. Vedo il tuo punto. È vero. Grazie per il testa a testa!
theWiseBro
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.