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);

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


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);

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.


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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.