专业的编程技术博客社区

网站首页 > 博客文章 正文

C++ 使迭代器与 STL 迭代器特性兼容

baijin 2024-10-12 02:10:14 博客文章 14 ℃ 0 评论

使迭代器与 STL 迭代器特性兼容

许多 STL 算法要求迭代器符合某些特性。不幸的是,这些要求在编译器、系统和 C++ 版本之间不一致。

对于我们的目的,我们将使用创建可迭代范围食谱中的类来说明这个问题。如果你在继续之前阅读了那个食谱,可能会更容易理解。

在 main() 中,如果我添加一个调用 minmax_element() 算法:

Seq<int> r{100, 110};
auto [min_it, max_it] = minmax_element(r.begin(), r.end());
cout << format("{} - {}\n", *min_it, *max_it);


它不编译。错误消息是模糊的、神秘的,并且是级联的,但如果你仔细观察,你会发现我们的迭代器不符合与此算法兼容的要求。

好的,我们来修复它。

如何做到这一点…

我们需要对我们的迭代器做一些简单的添加,使其与算法兼容。我们的迭代器需要满足前向迭代器的最低要求,所以让我们从这里开始:

我们几乎拥有前向迭代器所需的所有运算符。我们缺少的唯一一个是等同比较运算符 ==。我们可以很容易地通过 operator==() 重载将这个添加到我们的迭代器中:

bool operator==(const iterator& other) const {
    return value_ == other.value_;
}


有趣的是,这使得代码在某些系统上编译和运行,但在 Clang 上不行,我们得到错误消息:

No type named 'value_type' in 'std::iterator_traits<Seq<int>::iterator>'


这告诉我,我们需要在迭代器中设置特性。

迭代器特性类在迭代器类中查找一组类型定义(实现为 using 别名):

public:
    using iterator_concept = std::forward_iterator_tag;
    using iterator_category = std::forward_iterator_tag;
    using value_type = std::remove_cv_t<T>;
    using difference_type = std::ptrdiff_t;
    using pointer = const T*;
    using reference = const T&;


我倾向于将这些放在迭代器类 public: 部分的顶部,这样它们很容易看到。

现在我们有一个完全符合要求的前向迭代器类,代码在我所有的编译器上运行。

它是如何工作的…

using 语句是可能用于定义迭代器可以执行的功能的特性。让我们来看看它们中的每一个:

using iterator_concept = std::forward_iterator_tag;
using iterator_category = std::forward_iterator_tag;


前两个是类别和概念,都设置为 forward_iterator_tag。这个值表示迭代器符合前向迭代器规范。

一些代码不看这些值,而是寻找单独的设置和功能:

using value_type = std::remove_cv_t<T>;
using difference_type = std::ptrdiff_t;
using pointer = const T*;
using reference = const T&;


value_type 别名设置为 std::remove_cv_t<T>,这是值的类型,去除了任何 const 限定符。

difference_type 别名设置为 std::ptrdiff_t,作为指针差异的特殊类型。

pointer 和 reference 别名分别设置为指针和引用的 const 限定版本。

为大多数迭代器定义这些类型别名是基本要求。

还有更多…

值得注意的是,定义这些特性允许我们使用概念限制模板与我们的迭代器。例如:

template<typename T>
requires std::forward_iterator<typename T::iterator>
void printc(const T & c) {
    for(auto v : c) {
        cout << format("{} ", v);
    }
    cout << '\n';
}


这个打印我们序列的函数受到 forward_iterator concept 的限制。如果我们的类没有资格,它就不会编译。

我们也可以使用 ranges:: 版本的算法:

auto [min_it, max_it] = ranges::minmax_element(r);


这使得使用我们的迭代器更方便。

我们可以使用静态断言测试 forward_range 兼容性:

static_assert(ranges::forward_range<Seq<int>>);

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表