Search code examples
c++templatesiteratorfunction-templatesstdset

'Candidate template ignored: couldn't infer template argument' with std::set


I'm writing a function to manipulate a set with it's iterators, and it's giving me the error

candidate template ignored: couldn't infer template argument

The minimum reproducible code is as follows:

#include <bits/stdc++.h>
using namespace std;

template <typename T>
void foo(typename set<T>::iterator it1)
{
    // do something
}

int main() 
{
    set<int> aSet;
    foo(aSet.begin()); // no matching function call to 'foo'
}

I tried including <set> directly, changing the order of function definitions, but nothing solved it. And the weirdest thing is that I used the set iterator as parameter in other functions, and it worked. These other function declarations were something like this:

template <typename T>
void bar(set<T>& aSet, vector<T>& aVector, typename set<T>::iterator it);

(these exact parameters, in this exact order).


Solution

  • In foo the T in argument typename set<T>::iterator is a non-deduced contexts:

    ...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.

    The T is used in this non-deduced contexts and does not explicitly specify in foo what type it should deduce to, the compiler can not deuce the type; hence the error!

    That means, if you explicitly mention what T in foo, it will compile as mentioned in the quote.

    foo<int>(aSet.begin()); // compiles!
    

    or better, provide the iterator as template parameter

    template <typename Iterator> 
    void foo(Iterator it1)
    

    If you want to restrict the Iterator only for std::set<T>::iterator, you can SFINAE function. Here is an example code.

    #include <type_traits> // std::enable_if, std::is_same
    #include <iterator>    // std::iterator_traits
    
    template<typename Iterator>
    inline constexpr bool is_std_set_iterator = 
    std::is_same_v<Iterator, typename std::set<typename std::iterator_traits<Iterator>::value_type>::iterator> ||
    std::is_same_v<Iterator, typename std::set<typename std::iterator_traits<Iterator>::value_type>::const_iterator>;
    
    template <typename Iterator>
    auto foo(Iterator it1) -> std::enable_if_t<is_std_set_iterator<Iterator>, void>
    {
        // do something
    }
    

    On the other hand, in bar the compiler already deduced the first function argument to std::set<int>& aSet and sees the typename std::set<T>::iterator, which it can deduce to typename std::set<int>::iterator, because T was already deduced to int in the previous function argument. Hence, it works!


    As a side note, see the followings: