Eduard Rostomyan
Eduard Rostomyan

Reputation: 6546

Why compiler cannot deduce template template argument?

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)?

Upvotes: 2

Views: 701

Answers (2)

JeJo
JeJo

Reputation: 32722

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());

Upvotes: 5

R Sahu
R Sahu

Reputation: 206567

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.

Upvotes: 3

Related Questions