我正在尝试为列表迭代器和无符号整数重载+
运算符(用于实践)。下面的代码似乎工作正常。为了解释它应该做什么:如果iter
是一个迭代器,而k
是一个无符号整数,那么iter+k
(或operator+(iter, k)
)将返回一个通过递增{{1 }} k次。
iter
但是,当我这样做模板时:
list<int>::iterator operator+(list<int>::iterator iter, unsigned k)
{
while (k != 0)
{
++iter;
--k;
}
return iter;
}
运行类似
的简单内容template<typename T>
typename list<T>::iterator
operator+(typename list<T>::iterator iter, unsigned k)
{
while (k != 0)
{
++iter;
--k;
}
return iter;
}
我收到一个我无法破译的大错误。我也尝试无济于事:
int main(){
list<int> l{1,2};
list<int>::iterator q = l.begin();
q+1;
}
任何关于为什么发生这种情况以及如何解决它的想法都将受到赞赏!
答案 0 :(得分:3)
问题在于模板类型参数T
在{{3}}中,无法推导。要使其可推断,您可以执行以下操作:
template<typename T>
T operator+(T iter, unsigned k)
{ ... }
请注意,此重载可能会造成很多危害,因为T
不限于std::list<T>::iterator
。 SFINAE可以帮助:
template<typename T, typename = std::enable_if_t<
std::is_same_v<T, typename std::list<
typename std::iterator_traits<T>::value_type>::iterator>>>
T operator+(T iter, unsigned k)
{ ... }
添加。
operator+<int>(q, 1);
失败的原因更加微妙。首先,引用标准[temp.arg.explicit] / 8(另请参见示例):
但是,当使用带有显式模板参数的函数模板时,该调用将没有正确的语法形式。除非在调用时有一个名称可见的函数模板。如果看不到这样的名称,则该调用在语法上不正确,并且不应用依赖于参数的查找。如果某些此类名称可见,则将应用基于参数的查找,并且可能在其他名称空间中找到其他功能模板。
我们在这里有这样的功能模板,它是我们的operator+
。因此,ADL尝试在operator+<int>
中找到std::
。但是,使用operator+
实例化某些<int>
模板时,编译将失败。哪一个以及如何失败取决于具体的std库实现。
例如,gcc 8.1尝试实例化
template<class _Iterator> constexpr
std::move_iterator<_IteratorL> std::operator+(
typename std::move_iterator<_IteratorL>::difference_type,
const std::move_iterator<_IteratorL>&)
[with _Iterator = int]
并在其中某处失败
error: no type named 'reference' in 'struct std::iterator_traits<int>'
...
如果我们禁用ADL,它将按预期工作:
::operator+<int>(q, 1);
或
(operator+<int>)(q, 1);
答案 1 :(得分:2)
这与C ++模板类型推导的工作方式有关。由于语言的设置方式,编译器无法推断出list<T>::iterator
上下文中应自动生成的T。原因是,由于模板专门化,理论上list
的两个不同实例可能具有相同的嵌套iterator
类型(例如list<Pizwkat>
和list<Swibble>
) ,在这种情况下,编译器将无法确定T应该是Pizkwat
还是Swibble
,因为这两个选项似乎都可以工作。
尽管您可以考虑使用operator +
和std::next
,但我认为“添加一个仅适用于列表迭代器的std::advance
”这个普遍问题没有一个简单的解决方案。函数,适用于所有类型的迭代器。 (请注意,通常不为库类型添加自定义重载是一个好主意,因此即使您可以执行此操作,也不一定是您要解决的任何问题的最佳解决方案。)