【问题标题】:custom std::distance implementation自定义 std::distance 实现
【发布时间】:2020-05-02 13:01:35
【问题描述】:

我制作了“距离”模板函数来计算两个迭代器之间的距离。在函数中,我对情况进行了划分,一种用于随机访问迭代器,另一种用于随机访问迭代器。它可以很好地检测带有 iterator_traits 的迭代器的类型,但是我不能将此函数与双向迭代器一起使用。

这是我的距离函数

template <typename Iter>
ptrdiff_t distance(Iter first, Iter last)
{
    typedef typename std::iterator_traits<Iter>::iterator_category type;
    ptrdiff_t dist = 0;
    if (typeid(type) == typeid(std::random_access_iterator_tag)) {
        return last - first;
    }
    for (; first != last; ++first)
        dist++;
    return (dist);
}

这是我尝试测试的主要代码

int main(void)
{
    std::list<int> li;
    li.push_back(100);
    std::cout << ft::distance(li.begin(), li.end()) << std::endl;   
    return (0);
}

这是错误信息

./iterator.hpp:149:16: error: invalid operands to
      binary expression ('std::_List_iterator<int>' and
      'std::_List_iterator<int>')
                return (last - first);

我试图在没有“最后 - 第一个”的情况下制作距离函数,如下所示

template <typename Iter>
static ptrdiff_t subtract(Iter first, Iter last)
{
    return (last - first);
};

template <typename Iter>
ptrdiff_t distance(Iter first, Iter last)
{
    typedef typename std::iterator_traits<Iter>::iterator_category type;
    ptrdiff_t dist = 0;
    if (typeid(type) == typeid(std::random_access_iterator_tag)) {
        return subtract(first, last);
    }
    for (; first != last; ++first)
        dist++;
    return (dist);
}

但它仍然会产生同样的错误。我该如何解决这个问题?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    您的问题是if (typeid(type) == typeid(std::random_access_iterator_tag)) 必须是if constexpr,这样您就不会尝试为非随机访问迭代器编译last - first,但if constexpr (typeid(type) == typeid(std::random_access_iterator_tag)) 不会编译。

    不过,有一个example at cppreference(“第二版”)显示了另一种执行此操作的方法,如下所示:

    if constexpr (std::is_base_of_v<std::random_access_iterator_tag, type>)
        return last - first;
    else { ...
    

    这一切都适用于 C++17 及更高版本。如果您正在寻找 C++14 或更早版本的解决方案,那么上面的链接还显示了如何通过标签调度(“第一个版本”)来完成。

    【讨论】:

      【解决方案2】:

      标准库实现中的常用方法是使用迭代器标记来分派到特定的特化:

      template <class Iter, class Tag>
      std::ptrdiff_t distance_impl(Iter first, Iter last, Tag) {
          std::ptrdiff_t dist = 0;
          while (first != last) {
              ++dist;
              ++first;
          }
          return dist;
      }
      
      template <class Iter>
      std::ptrdiff_t distance_impl(Iter first, Iter last, std::random_access_iterator_tag) {
          return last - first;
      }
      
      template <class Iter>
      std::ptrdiff_t my_distance(Iter first, Iter last) {
          typedef typename std::iterator_traits<Iter>::iterator_category type;
          return distance_impl(first, last, type());
      }
      

      这比编写包含多个代码路径的函数要干净得多,这些代码路径可能是也可能不是编译版本的一部分。 (是的,constexpr if,我在看着你……)

      【讨论】: