我正在编写一个函数来使用它的迭代器操作一个集合,它给了我错误
candidate template ignored: couldn't infer template argument
最小可重现代码如下:
#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'
}
我尝试直接包含<set>
,更改函数定义的顺序,但没有任何解决方法。最奇怪的是,我在其他函数中使用了设置迭代器作为参数,并且它起作用了。 这些其他函数声明如下所示:
template <typename T>
void bar(set<T>& aSet, vector<T>& aVector, typename set<T>::iterator it);
(这些确切的参数,按这个确切的顺序)。
在foo
中,参数typename set<T>::iterator
中的T
是一个非推导的上下文:
。用于组成P的类型、模板和非类型值不参与模板参数推导,而是使用在其他地方推导或显式指定的模板参数。如果模板参数仅在非推导上下文中使用,并且未显式指定,则模板参数推导失败。
T
用于这种非推导上下文中,并且没有明确指定foo
它应该推导的类型,编译器无法对类型进行推断;因此错误!
这意味着,如果您明确提及foo
中的T
内容,它将按照引用中所述进行编译。
foo<int>(aSet.begin()); // compiles!
或者更好的是,提供迭代器作为模板参数
template <typename Iterator>
void foo(Iterator it1)
如果只想限制std::set<T>::iterator
Iterator
,则可以使用SFINAE功能。下面是一个示例代码。
#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
}
另一方面,在bar
中,编译器已经推导出了第一个函数参数std::set<int>& aSet
并看到了它可以推导typename std::set<int>::iterator
typename std::set<T>::iterator
,因为T
在前面的函数参数中已经推导出为int
。因此,它有效!
作为旁注,请参阅以下内容:
- 为什么我不应该 #include ?
- 为什么是"使用命名空间 std;"被认为是不良做法?