Contêiner STL com um tipo específico como argumento genérico

25

Existe alguma maneira que eu possa fazer uma função que pega um contêiner com um 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;
   }
}

e chamá-lo para todos os tipos de contêiner stl como entrada? como acima?

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

foo(strset);
foo(strvec);
foo(strlist);
chatzich
fonte
2
Sim, isso é chamado de função de modelo. ;)
Ulrich Eckhardt
2
Geralmente, é considerado melhor passar um par de iteradores (representando o início e o fim do contêiner, respectivamente). Desde que os iteradores atendam aos requisitos da função, (muitas vezes, existem algumas exceções), não importa de que tipo de contêineres foram obtidos.
Peter

Respostas:

21

Você pode criar fooum modelo de função usando um parâmetro de modelo para o tipo de contêiner.

por exemplo

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

VIVER

songyuanyao
fonte
Acho que podemos generalizar ainda mais. Veja minha resposta.
theWiseBro 19/02
A resposta de Lars é melhor porque também funciona com matrizes no estilo C.
Ayxan 19/02
11
@theWiseBro Sim, é uma boa ideia em geral. Mas eu acho que o OP só quer usá-lo com um tipo específico como std::string, então ..
songyuanyao 19/02
3
@theWiseBro exatamente. A OP disse que deveria trabalhar com um tipo específico . Portanto, não há benefício em generalizá-lo ainda mais.
M. Spiller
11
@theWiseBro Entendo o que você quis dizer. Não tenho certeza sobre a intenção original do OP, ele apenas disse que quer um tipo específico; pode ser necessário explicá-lo para o OP. :)
songyuanyao 19/02
6

Dependendo se você deseja sobrecarregar foopara outros casos ou não

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

Você pode usar um teste diferente para std::is_same, como std::is_convertiblepermitir

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

Você pode considerar o uso de iteradores. Um resultado intermediário pode 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';
  }
}

Agora, usando um modelo que pode ser chamado:

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

Acabamos de aprender a usar o que o STL já oferece.

user1624886
fonte
-1

Adicionando à resposta de @ songyuanyao, acho que podemos generalizá-la ainda mais para:

template<template<typename...> typename C, typename ... D>
void foo(const C<D...> &cont)
{
   for(const auto& val: cont) {
      std::cout << val << std::endl;
   }
}
theWiseBro
fonte
11
Isso não restringe o tipo de elemento a std :: string, portanto não responde à pergunta.
Sasha
@ Sasha É verdade que isso não é corrigido para std :: string, mas é mais generalizado. O OP quer usar um tipo específico. Digamos que hoje ele esteja usando std :: string e amanhã ele queira usar um MyCustomString. Isso não seria mais fácil de manter, pois ele só precisa editar o código em um único local?
theWiseBro 20/02
Mas isso não mostra como restringir-lo para qualquer std :: string ou elementos MyCustomString - eo consulente especificamente queria levar "um recipiente com um tipo específico ". Como é, ele aceitará qualquer tipo que seja um modelo e, nesse ponto, por que não apenas modelá-lo em um único <typename C>? Isso é muito mais simples e um pouco mais generalizado - por exemplo, o seu terá um std :: string (também conhecido como std :: basic_string <char>) como contêiner, mas não uma estrutura MyCustomString personalizada, portanto, não é totalmente genérico.
Sasha
E se a função espera os elementos a serem std :: string, permitindo aos usuários passar um std :: tuple <int, double, std :: nullptr_t> torna mais difícil de usar e manter.
Sasha
@Sasha hmm. Eu entendo o seu ponto. Isso é verdade. Obrigado pela atenção!
theWiseBro 20/02