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

时间:2018-09-01 18:46:02

标签: c++ iterator

我正在尝试为列表迭代器和无符号整数重载+运算符(用于实践)。下面的代码似乎工作正常。为了解释它应该做什么:如果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;
}

任何关于为什么发生这种情况以及如何解决它的想法都将受到赞赏!

2 个答案:

答案 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”这个普遍问题没有一个简单的解决方案。函数,适用于所有类型的迭代器。 (请注意,通常不为库类型添加自定义重载是一个好主意,因此即使您可以执行此操作,也不一定是您要解决的任何问题的最佳解决方案。)