25

Is there any way that I can make a function which takes a container with a specific type (lets say std::string) as a parameter

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

and call it for every type of stl container as input? like above?

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

foo(strset);
foo(strvec);
foo(strlist);
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
chatzich
  • 1,083
  • 2
  • 11
  • 26
  • 2
    Yep, it's called a template function. ;) – Ulrich Eckhardt Feb 19 '20 at 09:57
  • 2
    It is often considered better to pass a pair of iterators (representing beginning and one-past-the-end of the container, respectively). As long as iterators meet requirements of the function, it (often, there are some exceptions) doesn't matter what type of containers they were obtained from. – Peter Feb 19 '20 at 10:16

4 Answers4

21

You can make foo a function template taking a template template parameter for the container type.

e.g.

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

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • I think we can generalize it even further. See my answer. – theWiseBro Feb 19 '20 at 10:06
  • Lars' [answer](https://stackoverflow.com/a/60297543/10147399) is better because it also works with C-style arrays. – Aykhan Hagverdili Feb 19 '20 at 10:07
  • 1
    @theWiseBro Yes it's a good idea in general. But I think OP just wants to use it with specific type as `std::string`, so.. – songyuanyao Feb 19 '20 at 10:07
  • @songyuanyao OP gave std::string as an example. Let's say if he wishes to change this to say `std::vector` someday, he'll have to alter this method as well. I think a more generalized code would have been better. – theWiseBro Feb 19 '20 at 10:11
  • @theWiseBro following that path using `void*` as type for parameter should be the way to go. – MSpiller Feb 19 '20 at 10:13
  • @Ayxan nope it won't. Well at least not the iteration part. – theWiseBro Feb 19 '20 at 10:13
  • @M.Spiller no need to go that far xD. OP mentioned he'll be using a container. I see no benefit whatsoever to move to `void*` – theWiseBro Feb 19 '20 at 10:15
  • 3
    @theWiseBro exactly. OP said that it should work with _one specific type_. Therefore there is no benefit to generalize it further. – MSpiller Feb 19 '20 at 10:16
  • 1
    @theWiseBro I understand what you meant. I'm not sure about OP's original intent, he just said want one specific type; you might need to explain it to OP. :) – songyuanyao Feb 19 '20 at 10:16
  • I am trying to pass a `std::set` undefined reference to `void foo(std::set, std::allocator > > const&)'` – chatzich Feb 19 '20 at 10:57
  • @chatzich Could you make a [mcve](https://stackoverflow.com/help/minimal-reproducible-example) for it? I tried it with `std::set` [here](https://wandbox.org/permlink/VSIzX5gi2Hb2ZNAW) and seems working fine. – songyuanyao Feb 19 '20 at 11:00
  • @theWiseBro you can pass an array with reference and the iteration part will work. – Aykhan Hagverdili Feb 19 '20 at 11:43
  • too complex. a simple template would serve the purpose. See @Caleth 's reply. – Red.Wave Feb 24 '20 at 14:03
6

Depending on if you want to overload foo for other cases or not

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

You might use a different test to std::is_same, such as std::is_convertible to allow

std::vector<char *> c_strings;
foo(c_strings);
Caleth
  • 52,200
  • 2
  • 44
  • 75
0

You may want to consider using iterators instead. An intermediate result may look like

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

Now using a callable template:

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

We just learned to use what the STL already offers.

-1

Adding on to @songyuanyao's answer, I think we can generalize it further to:

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,439
  • 12
  • 11
  • 1
    This doesn't restrict the element type to std::string, so it doesn't answer the question. – Sasha Feb 20 '20 at 14:34
  • @Sasha It's true that this is not fixed to std::string but it's more generalized. The OP wants to use a specific type. Say today he's using std::string and tomorrow he wants to use a MyCustomString instead. Wouldn't this prove easier to maintain since he only has to edit the code at a single place? – theWiseBro Feb 20 '20 at 14:42
  • But this doesn't show how to restrict it to *either* std::string or MyCustomString elements - and the querent specifically wanted to take "a container *with a specific type*". As is, it'll accept any type that happens to be a template, and at that point why not just template it on a single instead? That's much simpler and slightly more generalised - e.g. yours will take an std::string (aka std::basic_string) as the container but not a custom struct MyCustomString, so it's not fully generic. – Sasha Feb 20 '20 at 14:48
  • And if the function expects the elements to be std::string, allowing users to pass an std::tuple makes it *harder* to use and maintain. – Sasha Feb 20 '20 at 14:52
  • @Sasha hmm. I see your point. That's true. Thanks for the heads up! – theWiseBro Feb 20 '20 at 14:56