迭代器和无符号整数的重载 + 运算符

Overloading + operator for iterators and unsigned integers

本文关键字:运算符 重载 无符号整数 迭代器      更新时间:2023-10-16

我正在尝试为列表迭代器和无符号整数重载+运算符(用于练习(。下面的代码似乎工作正常。解释它应该做什么:如果iter是一个迭代器并且k一个无符号整数,那么iter+k(或operator+(iter, k)(返回一个从递增iterk次中获得的迭代器。

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;
}

我收到一个无法破译的大错误。我也尝试无济于事:

int main(){
list<int> l{1,2};
list<int>::iterator q = l.begin();
operator+<int>(q,1);
}

关于为什么会发生这种情况以及如何解决它的任何想法将不胜感激!

问题是模板类型参数T处于非推导上下文中,无法推导。要使其可推导,您可以这样做:

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试图在std::中找到operator+<int>。但是,当使用<int>实例化某些operator+模板时,编译将失败。哪一个以及如何失败取决于特定的 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);

这与模板类型推导C++工作方式有关。由于语言的设置方式,编译器无法推断出 T 应该在list<T>::iterator的上下文中自动推断出什么。原因是,由于模板专用化,理论上两个不同的list实例化可能具有相同的嵌套iterator类型(例如,list<Pizwkat>list<Swibble>(,在这种情况下,编译器将无法确定 T 应该Pizkwat还是Swibble,因为这两个选项都可能有效。

我不相信有一个简单的解决方案来解决"添加一个仅适用于列表迭代器的operator +"的一般问题,尽管您可以考虑使用std::nextstd::advance函数,它们适用于所有类型的迭代器。(请注意,为库类型添加自定义重载通常不是一个好主意,因此即使您可以执行此操作,它也可能不是您尝试解决的任何问题的最佳解决方案。