0

I tried to write a generic print function in C++14. But template printItem function can not instantiate.

template <class T>
void printItem(T t)
{
    std::cout << t << std::endl;
}

template <class T>
void printVector(T t)
{
    for_each(t.begin(), t.end(), printItem);
}

int main()
{
    std::vector<std::string> vs = {"word1", "word2"};

    printVector(vs);
}

This code causes a compiler error:

no matching function for call to 
'for_each(std::vector<std::__cxx11::basic_string<char> >::iterator, std::vector<std::__cxx11::basic_string<char> >::iterator, <unresolved overloaded function type>)'
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Once you solve the "" issue, you still have another problem. `T` in `printItem` is `std::string`, so `*t` will not compile since `std::string` does not have an `operator*` implemented. Use `<< t` instead of `<< *t`. – Remy Lebeau Apr 23 '20 at 17:31
  • actually star operator was a typo, I fixed after post. Thanks a lot. – Mustafa COKER Apr 24 '20 at 08:25

2 Answers2

4

printItem is a function template, and it cannot deduce the argument type, so you need to specify that, like this:

for_each(t.begin(), t.end(), printItem<typename T::value_type>);

Additionally, there appears to be a typo in printItem. You don't need to dereference t at all.

If you are not using this function anywhere else, then you can define it inline using a lambda, like this:

for_each(t.begin(), t.end(), [](auto s) { std::cout << s << std::endl; });

Note that the lambda operator() is also templated in this case, however the type can be deduced, so it's fine.

Also, std::for_each is often a code smell, in my opinion. It can be replaced by the much more readable:

for (auto const &s : t)
    std::cout << s << std::endl; 
cigien
  • 57,834
  • 11
  • 73
  • 112
1

There is no way for the compiler to know which printItem specialisation you wanted, with the information available to it according to the standard at that point.

So, this syntax is not available here.

This is what you need:

for_each(t.begin(), t.end(), printItem<typename T::value_type>);

Being able to do this kind of thing is exactly why containers are required to define type aliases like value_type; you end up needing them all over the place.

A limitation in the language? Perhaps. But it does help avoid corner cases.

Having to use typename here certainly is an annoyance, though.


Your code, to be honest, would be a lot easier and clearer if you just used a loop:

for (const auto& item : t)
   printItem(item);

In the time of ranged-for and all that lovely stuff, for_each is not all that useful for common cases any more.


Also, take the arguments by reference-to-const, and remove the erroneous dereference. I'll also remove the repeated std::endl, which is performing stream flushing you don't need.

template <class T>
void printItem(const T& t)
{
    std::cout << t << '\n';
}

template <class T>
void printVector(const T& t)
{
    for (const auto& item : t)
       printItem(t);
}

int main()
{
    std::vector<std::string> vs = {"word1", "word2"};
    printVector(vs);
}

Finally, it's a good idea to let functions like printItem take a reference to the stream you want to use, so that you could pick (say) a std::stringstream rather than only std::cout.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35