首页 > 解决方案 > 为迭代器和无符号整数重载 + 运算符

问题描述

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

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

标签: c++iterator

解决方案


问题是模板类型参数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 尝试operator+<int>std::. 但是,当某些operator+模板使用<int>. 哪一个以及它如何失败,取决于特定的标准库实现。

例如,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);

推荐阅读