Search code examples
c++c++11templatesfunction-templatestemplate-templates

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


Solution

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