Search code examples
c++templatestemplate-argument-deduction

Can't deduce template type


I'm trying to pass an iterator as a template parameter to a template method, but the compiler complains that:

error C2783: 'void Test::Assert(std::vector<T>::const_iterator)':
could not deduce template argument for 'T'

The code that produces the error is:

#include "stdafx.h"
#include <iostream>
#include <vector>

class Test
{
    public:
        template <typename T>
        void Assert(typename std::vector<T>::const_iterator it)
        {
            std::cout << *it << std::endl;
        }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Test test;

    std::vector<double> myVec;

    test.Assert(myVec.cbegin());

    return 0;
}

I'm guessing there is a simple way to make this work, since most of the std algorithms can deduce type from iterator.


Solution

  • The reason is that the form you have T in is a non-deduced context:

    template <typename T>
    void Assert(typename std::vector<T>::const_iterator it)
    

    Consider a simpler case to understand why:

    struct A { using type = int; };
    struct B { using type = int; };
    struct C { using type = int; };
    
    template <typename T>
    void Assert(typename T::type it) { ... }
    
    Assert(5);
    

    What should T deduce as? It's impossible to determine. You'd have to explicitly provide the type... as something like Assert<A>(5).

    See also What is a nondeduced context?

    since most of the std algorithms can deduce type from iterator.

    That's because the standard algorithms just deduce the iterator type, not the container type. For instance std::find is just:

    template <class InputIt, class T>
    InputIt find( InputIt first, InputIt last, const T& value );
    

    There is no concept of "container" here at all - it's just the iterator type that needs to be deduced. That's part of the beauty of the algorithms library.

    So if what you want to do is just output the contents of the iterator, the correct function would just be:

    template <typename Iterator>
    void Assert(Iterator it)
    {
        std::cout << *it << std::endl;
    }
    

    When you call Assert(myVec.cbegin()), Iterator will get deduced as std::vector<double>::const_iterator, which is exactly what you want.