Контейнер STL с определенным типом в качестве универсального аргумента

25

Есть ли способ, которым я могу сделать функцию, которая принимает контейнер с определенным типом (скажем, std::stringв качестве параметра)

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

и вызвать его для каждого типа контейнера STL в качестве ввода? как выше?

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

foo(strset);
foo(strvec);
foo(strlist);
chatzich
источник
2
Да, это называется шаблонной функцией. ;)
Ульрих Экхардт
2
Часто считается, что лучше передать пару итераторов (представляющих начало и один конец конца контейнера соответственно). Пока итераторы отвечают требованиям функции, она (часто с некоторыми исключениями) не имеет значения, из какого типа контейнеров они были получены.
Питер

Ответы:

21

Вы можете создать fooшаблон функции, используя параметр шаблона шаблона для типа контейнера.

например

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

ЖИТЬ

songyuanyao
источник
Я думаю, что мы можем обобщить это еще дальше. Смотри мой ответ.
theWiseBro
Ответ Ларса лучше, потому что он также работает с массивами в стиле Си.
Ayxan
1
@theWiseBro Да, это хорошая идея в целом. Но я думаю, что OP просто хочет использовать его с определенным типом, как std::string, так что ..
songyuanyao
3
@ theWiseBro точно. ОП сказал, что он должен работать с одним конкретным типом . Поэтому нет смысла обобщать его дальше.
М. Спиллер
1
@ theWiseBro Я понимаю, что вы имели в виду. Я не уверен насчет первоначального намерения О.П., он просто сказал, хочу один конкретный тип; вам может понадобиться объяснить это ОП. :)
songyuanyao
6

В зависимости от того, хотите ли вы перегрузить fooдля других случаев или нет

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

Вы можете использовать другой тест std::is_same, например, std::is_convertibleчтобы разрешить

std::vector<char *> c_strings;
foo(c_strings);
Caleth
источник
0

Вы можете рассмотреть возможность использования итераторов вместо этого. Промежуточный результат может выглядеть так

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

Теперь с помощью вызываемого шаблона:

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

Мы только что научились использовать то, что уже предлагает STL.

user1624886
источник
-1

Добавляя к ответу @ songyuanyao, я думаю, что мы можем обобщить его следующим образом:

template<template<typename...> typename C, typename ... D>
void foo(const C<D...> &cont)
{
   for(const auto& val: cont) {
      std::cout << val << std::endl;
   }
}
theWiseBro
источник
1
Это не ограничивает тип элемента std :: string, поэтому не отвечает на вопрос.
Саша
@Sasha Это правда, что это не фиксируется в std :: string, но это более обобщенно. ОП хочет использовать определенный тип. Скажем, сегодня он использует std :: string, а завтра он хочет вместо этого использовать MyCustomString. Разве это не будет легче поддерживать, поскольку он должен редактировать код только в одном месте?
theWiseBro
Но это не показано , как ограничить его либо станд :: строка или MyCustomString элементов - и кверентом специально хотел взять «контейнер с типом конкретного ». Таким образом, он будет принимать любой тип, который является шаблоном, и в этот момент, почему бы не использовать его вместо одного <typename C>? Это намного проще и немного более обобщенно - например, ваш будет принимать std :: string (aka std :: basic_string <char>) в качестве контейнера, но не пользовательскую структуру MyCustomString, поэтому он не является полностью универсальным.
Саша
И если функция ожидает, что элементы будут std :: string, что позволяет пользователям передавать std :: tuple <int, double, std :: nullptr_t> усложняет использование и обслуживание.
Саша
@ Саша хм. Я понимаю вашу точку зрения. Это правда. Спасибо за головы!
TheWiseBro