Home > Back-end >  'Candidate template ignored: couldn't infer template argument' with std::set
'Candidate template ignored: couldn't infer template argument' with std::set

Time:09-17

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

CodePudding user response:

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:

  • Related