Search code examples
c++templatespredicatetemplate-argument-deduction

Cannot infer template argument 'T'


I have pretty simple code:

#include <iostream>
#include <list>
using namespace std;

template <typename T, typename Function>
int ex_count(typename list<T>::iterator v, typename list<T>::iterator v2, Function match)
{
    int count = 0;
    while(v != v2)
    {
        if(match(*v))
            count++;
        ++v;
    }
    return count;
}

template <typename T>
class ex_eq
{
    public:
    ex_eq(const T &n_target = T())
    {
        target = n_target;
    }
    bool operator() (const T &a)
    {
        return a == target;
    }
    private:
    T target;
};

int main() 
{
    list<int> v;
    list<int>::iterator iv;
    int value;
    while (cin >> value)
        v.push_back(value);
    int target = *v.begin();
    int N = ex_count(v.begin(), v.end(), ex_eq<int>(target));
    cout << "Found " << N << " instances of " << target << "\n";
}

I recently implemented a predicate object Function match into the int ex_count() function, but for whatever reason this breaks the code. The only error I'm getting is that the argument 'T' cannot be inferred, for int ex_count(), even though this was working fine using a constant instead of an object (Function match).

I should note that I'm not looking for advice for anything other than the question I am asking. The code is overly complicated and pointless--I'm aware, but this is not my design.

What's the issue?

Error for clarity:

note: candidate template ignored: couldn't infer template argument 'T'
int ex_count(typename list<T>::iterator v, typename list<T>::iterator v2, Function match)

Edit:

I've been told that it cannot be deduced because of the :: operator, but then why is it that this version of the code only works when using the :: operator:

#include <iostream>
#include <list>
using namespace std;

template <typename T>
int ex_count(typename list<T>::iterator v, typename list<T>::iterator v2, T target)
{
    int count = 0;
    while(v != v2)
    {
        if(*v == target)
            ++count;
        ++v;
    }
    return count;
}

int main() {
    list<int> v;
    list<int>::iterator iv;
    int value;
    while (cin >> value)
        v.push_back(value);
    int target = *v.begin();
    int N = ex_count(v.begin(), v.end(), target);
    cout << "Found " << N << " instances of " << target << "\n";
}

This version does not use a function object, but instead just uses target. However, if I try to change ex_count arguments from list<T>::iterator to just T, I get the error:

note: candidate template ignored: deduced conflicting types for parameter 'T'
      ('std::__1::__list_iterator<int, void *>' vs. 'int')
int ex_count(T v, T v2, T target)

But it compiles just fine the way I have it written above.

So what causes this?


Solution

  • The template parameter T can't be deduced because of non-deduced context.

    In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

    1. The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

    You can specify the template argument explicitly to bypass the template argument deduction,

    int N = ex_count<int>(v.begin(), v.end(), ex_eq<int>(target));
    

    Or change the function template to specify the iterator type as the template parameter directly. (And it could be used with any iterators, including raw pointers.)

    template <typename Iterator, typename Function>
    int ex_count(Iterator v, Iterator v2, Function match)
    {
        int count = 0;
        while(v != v2)
        {
            if(match(*v))
                count++;
            ++v;
        }
        return count;
    }
    

    EDIT

    For

    template <typename T>
    int ex_count(typename list<T>::iterator v, typename list<T>::iterator v2, T target)
    

    Template argument deduction won't be performed on non-deduced context, then T will be deduced only on the 3rd function argument. Given ex_count(v.begin(), v.end(), target); T will be deduece as int, and v.begin() and v.end() match the type list<int>::iterator, everything is fine.

    For

    template <typename T>
    int ex_count(T v, T v2, T target)
    

    There's no non-deduced context and T will be deduced on all the function arguments. Given ex_count(v.begin(), v.end(), target);, on the 1st and 2nd function arguments T will be deduced as list<int>::iterator, on the 3rd one it'll be deduced as int, they are conflicting.

    If you seperate the template parameters then it'll be fine.

    template <typename Iterator, typename T>
    int ex_count(Iterator v, Iterator v2, T target)