Contenedor STL con un tipo específico como argumento genérico

25

¿Hay alguna manera de que pueda hacer una función que tome un contenedor con un tipo específico (digamos std::string) como parámetro

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

y llamarlo para cada tipo de contenedor stl como entrada? como arriba?

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

foo(strset);
foo(strvec);
foo(strlist);
Chatzich
fuente
2
Sí, se llama una función de plantilla. ;)
Ulrich Eckhardt
2
A menudo se considera mejor pasar un par de iteradores (que representan el principio y el pasado del contenedor, respectivamente). Mientras los iteradores cumplan con los requisitos de la función, (a menudo, hay algunas excepciones) no importa de qué tipo de contenedores se obtuvieron.
Peter

Respuestas:

21

Puede hacer foouna plantilla de función tomando un parámetro de plantilla de plantilla para el tipo de contenedor.

p.ej

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

EN VIVO

songyuanyao
fuente
Creo que podemos generalizarlo aún más. Mira mi respuesta.
theWiseBro
La respuesta de Lars es mejor porque también funciona con matrices de estilo C.
Ayxan
1
@theWiseBro Sí, es una buena idea en general. Pero creo que OP solo quiere usarlo con un tipo específico como std::string, así que ...
songyuanyao
3
@theWiseBro exactamente. OP dijo que debería funcionar con un tipo específico . Por lo tanto, no hay beneficio para generalizarlo más.
M. Spiller
1
@theWiseBro Entiendo lo que quisiste decir. No estoy seguro sobre la intención original de OP, solo dijo que quería un tipo específico; Es posible que deba explicarlo a OP. :)
songyuanyao
6

Dependiendo de si desea sobrecargar foopara otros casos o no

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

Puede usar una prueba diferente para std::is_same, como std::is_convertiblepermitir

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

Es posible que desee considerar el uso de iteradores en su lugar. Un resultado intermedio puede parecer

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

Ahora usando una plantilla invocable:

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

Acabamos de aprender a usar lo que el STL ya ofrece.

usuario1624886
fuente
-1

Agregando a la respuesta de @ songyuanyao, creo que podemos generalizarlo aún más a:

template<template<typename...> typename C, typename ... D>
void foo(const C<D...> &cont)
{
   for(const auto& val: cont) {
      std::cout << val << std::endl;
   }
}
theWiseBro
fuente
1
Esto no restringe el tipo de elemento a std :: string, por lo que no responde la pregunta.
Sasha
@Sasha Es cierto que esto no está arreglado para std :: string pero es más generalizado. El OP quiere usar un tipo específico. Digamos que hoy está usando std :: string y mañana quiere usar MyCustomString en su lugar. ¿No resultaría más fácil de mantener, ya que solo tiene que editar el código en un solo lugar?
theWiseBro
Pero esto no muestra cómo restringirlo a los elementos std :: string o MyCustomString, y el consultante específicamente quería tomar "un contenedor con un tipo específico ". Tal como está, aceptará cualquier tipo que sea una plantilla, y en ese punto, ¿por qué no simplemente crear una plantilla en un solo <typename C>? Eso es mucho más simple y un poco más generalizado, por ejemplo, el suyo tomará un std :: string (también conocido como std :: basic_string <char>) como el contenedor, pero no una estructura MyCustomString personalizada, por lo que no es completamente genérico.
Sasha
Y si la función espera que los elementos sean std :: string, lo que permite a los usuarios pasar std :: tuple <int, double, std :: nullptr_t> hace que sea más difícil de usar y mantener.
Sasha
@ Sasha hmm. Entiendo tu argumento. Es verdad. ¡Gracias por el aviso!
theWiseBro