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: