c++ - 在 C++20 中,如何编写连续迭代器?

标签 c++ stl iterator c++20 contiguous

C++20 对 std::contiguous_iterator_tag 有明确的库支持.一些 STL 算法(例如 std::copy )可以在连续迭代器上表现得更好。但是,我不清楚程序员应该如何访问这个新功能。
为了论证起见,让我们假设我们有一个完全符合 C++20 的库实现。我想编写最简单的连续迭代器。
Here's my first attempt.

#include <iterator>

class MyIterator {
    int *p_;
public:
    using value_type = int;
    using reference = int&;
    using pointer = int*;
    using difference_type = int;
    using iterator_category = std::contiguous_iterator_tag;
    int *operator->() const;
    int& operator*() const;
    int& operator[](int) const;
    MyIterator& operator++();
    MyIterator operator++(int);
    MyIterator& operator--();
    MyIterator operator--(int);
    MyIterator& operator+=(int);
    MyIterator& operator-=(int);
    friend auto operator<=>(MyIterator, MyIterator) = default;
    friend int operator-(MyIterator, MyIterator);
    friend MyIterator operator+(MyIterator, int);
    friend MyIterator operator-(MyIterator, int);
    friend MyIterator operator+(int, MyIterator);
};

namespace std {
    int *to_address(MyIterator it) {
        return it.operator->();
    }
}

static_assert(std::contiguous_iterator<MyIterator>);  // FAILS
这在 GCC/libstdc++ 和 MSVC/STL 上都失败了;但它应该吗?
对于我的下一次尝试,我专门 pointer_traits<MyIterator> . MyIterator实际上不是指针,所以我没有在 pointer_traits 中放入任何东西除了图书馆需要的一个功能。 This is my second attempt :
#include <iterator>

class MyIterator {
    ~~~
};

template<>
struct std::pointer_traits<MyIterator> {
    int *to_address(MyIterator it) {
        return it.operator->();
    }
};

static_assert(std::contiguous_iterator<MyIterator>);  // OK!
这是我应该做的吗?感觉非常hacky。 (需要明确的是:我的第一次失败尝试也感觉非常糟糕。)
我错过了一些更简单的方法吗?
特别是,MyIterator有什么办法吗?本身来保证它是连续的,只使用成员和 friend 以及可以在类的主体中定义的东西?就像,如果 MyIterator定义在某个深度嵌套的命名空间中,我不想为了打开 namespace std 而一路突破到顶级命名空间.

编辑添加:Glen Fernandes 告诉我有一个更简单的方法——我应该添加一个 element_type类型定义,like this! (而且我可以在 C++20 中删除 iterator_traits ' 大 5 个 typedef 中的 3 个。)这看起来更好!
#include <iterator>

class MyIterator {
    int *p_;
public:
    using value_type = int;
    using element_type = int;
    using iterator_category = std::contiguous_iterator_tag;
    int *operator->() const;
    int& operator*() const;
    int& operator[](int) const;
    MyIterator& operator++();
    MyIterator operator++(int);
    MyIterator& operator--();
    MyIterator operator--(int);
    MyIterator& operator+=(int);
    MyIterator& operator-=(int);
    friend auto operator<=>(MyIterator, MyIterator) = default;
    friend int operator-(MyIterator, MyIterator);
    friend MyIterator operator+(MyIterator, int);
    friend MyIterator operator-(MyIterator, int);
    friend MyIterator operator+(int, MyIterator);
};

static_assert(std::contiguous_iterator<MyIterator>);  // OK!

另外,我看到了一些关于使用成员 typedef iterator_concept 的内容。而不是 iterator_category .我为什么要提供 MyIterator::iterator_concept ? (我不是在这里要求完整的历史解释;只是一个简单的最佳实践指南“不要忘记 iterator_concept”或“是的,因为它有助于 X”会很好。)

最佳答案

C++ 概念作为语言特性的主要好处之一是概念定义告诉您需要提供什么。 std::contiguous_iterator is no different .是的,您可能需要深入研究 10 多个其他概念定义层才能深入了解您需要提供的基本术语。但它们就在语言中。
简而言之,连续迭代器是随机访问迭代器:

  • 其标签源自 contiguous_iterator
  • 谁的reference_type是对其 value_type 的左值引用(即:不是代理迭代器或生成器)。
  • standard library function std::to_address 可以被调用以将任何有效的迭代器(即使是不可解引用的)转换为指向迭代器引用的元素或指向最后一个指针的指针。

  • 不幸的是,满足 #3 不能通过特化来完成 std::to_address直接地。您必须使用专业 pointer_traits::to_address为您的迭代器类型或给您的迭代器一个 operator->重载。
    后者更容易做到,尽管 operator-> std::contiguous_iterator 没有其他要求, 对于这样的迭代器类型是有意义的:
    class MyIterator
    {
     ...
      int const *operator->() const;
    };
    
    
    仅供引用:如果您基于概念约束模板,而不仅仅是 static_assert,大多数编译器会给您更多有用的错误消息。正在研究它。像这样:
    template<std::contiguous_iterator T>
    void test(const T &t);
    
    test(MyIterator{});
    

    关于c++ - 在 C++20 中,如何编写连续迭代器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65712091/

    相关文章:

    c++ - 如何改变MFC PropertySheet的字体

    c++ - 标准 : :array -- difference between size() and max_size()

    c++ - 通用迭代器在不使用 C++ 模板的情况下访问 vector 的元素

    c++ - 无法在 cbegin 中定义 initializer_list

    c++ - 在 C++ 中避免标签缩进

    Visual Studio中的C++运行结构

    c++ - 一个函数可以同时接受迭代器和反向迭代器作为参数吗

    c++ - 为什么编译器推迟 std::list 释放?

    sql - 如何为给定的一组行创建某种迭代器(或人工 ID)?

    c++ - 如何从 rapidjson::Document 获取中文 wstring?