2

I want to write a function that takes a generic container with any type and prints it.
Let's leave for a moment that it won't work for some associative containers and focus on the issue:

template<template<typename> typename Cont, typename T>
void print(const Cont<T>& cont)
{
   for (const auto it : cont)
   {
      cout << it << " ";
   }
   cout << endl;
}

int main()
{
   vector<string> v;
   print(v);
}

The error states:

error C2672: 'print': no matching overloaded function found    
error C3207: 'print': invalid template argument for 'Cont', class template expected

Can anyone please explain why the compiler cannot deduce the types here?
Even when I explicitly state print<vector<string>>(v)?

JeJo
  • 30,635
  • 6
  • 49
  • 88
Eduard Rostomyan
  • 7,050
  • 2
  • 37
  • 76
  • 2
    You don't need template template parameters. `template void print(const Cont& cont) {...}` would work just as well. – Igor Tandetnik Aug 16 '20 at 16:30
  • 1
    If you see e.g. [this `std::vector` reference](https://en.cppreference.com/w/cpp/container/vector) you will see that it has *two* template arguments, not the single one you specify. And different standard containers have different number of template arguments. – Some programmer dude Aug 16 '20 at 16:30

2 Answers2

5

std::vector has more than one class-template-args.

template<
    class T,                // -----------> you have mentioned!
    class Allocator = std::allocator<T>  // -----> this didn't!
> class vector;

But you have provided only one. This is the reason for the no matching overloaded compiler error.

In order to fix the issue, you need to provide the variadic args in the template template arg.

template<template<typename...> typename Cont, typename T>
//       ^^^^^^^^^^^^^^^^^^^^
void print(const Cont<T>& cont)
{
   for (const auto& it : cont) {
      std::cout << it << " ";
   }
   std::cout << std::endl;
}

(See a demo)


However, you could have done this simply

template <typename Cont>
void print(const Cont& cont) 
{ 
   // ...
}

Or like the standard way, passing the begin and end iterator of the container to the function

#include <algorithm>  // std::copy
#include <iterator>   // std::iterator_traits

template<typename Iterator>
constexpr void print(const Iterator begin, const Iterator end) noexcept
{
   using ValueType = typename std::iterator_traits<Iterator>::value_type;
   std::copy(begin, end, std::ostream_iterator<ValueType>(std::cout, " "));
   std::cout << "\n";
}

and call it like

print(v.begin(), v.end());
JeJo
  • 30,635
  • 6
  • 49
  • 88
  • 2
    Or else just drop template template parameters altogether, and make it `template void print(const Cont& cont) {...}` – Igor Tandetnik Aug 16 '20 at 16:30
  • Unless he needs T for some reason that was not included into MCE. That's usually the reason for template template parameter, but standard types offer work-arounds usually – Swift - Friday Pie Aug 16 '20 at 16:32
  • 1
    @Swift-FridayPie Even so, that could be obtained with `typename Cont::value_type` or `decltype(*std::begin(cont))`. The latter in particular doesn't need any more support from `Cont` than the range-for loop itself already does. – Igor Tandetnik Aug 16 '20 at 16:33
  • @IgorTandetnik yeah, just thought of that. I tend to add such into my creations - custom types too, but everyone got own roaches in their style. Next moment I see someone asks why I added those declaration and then they happily use pattern above instead of nested type – Swift - Friday Pie Aug 16 '20 at 16:34
3

std::vector is defined as

template<
    class T,
    class Allocator = std::allocator<T>
> class vector;

std::vector is a class template with two template parameters. It does not match the template template parameter of your function.

Change it to

template <typename Cont>
void print(const Cont& cont) 
{
    for (const auto& elem : cont) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

You can deduce T from the container, if that is necessary.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
R Sahu
  • 204,454
  • 14
  • 159
  • 270