Boost C++ 库

...世界上最受推崇、设计最精湛的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

第 36 章。Boost.STLInterfaces - Boost C++ 函数库
PrevUpHomeNext

第 36 章。Boost.STLInterfaces

Zach Laine

根据 Boost 软件许可证版本 1.0 发布。(参见随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制)

编写 STL 迭代器、视图和容器是一项非常困难的工作。有很多地方可能出现细微的错误。它也很繁琐,当然这使得它容易出错。

迭代器有大量的 typedef 和操作,尽管给定迭代器的所有操作最多可以用四个操作(通常只有三个)来实现。编写所有其他操作会产生非常相似的代码,难以审查,并且几乎要求您为每个迭代器编写完整的测试。

编写视图类型,如 std::ranges 中的视图类型,也需要付出很多努力,考虑到视图类型的大部分 API 可以从 begin()end() 派生。C++20 有一个模板可以完全做到这一点,即 std::ranges::view_interface;Boost.STLInterfaces 提供了一个对 C++20 之前的版本友好的实现。

由于不同编译器在实现其 C++20 视图适配器的 operator| 支持方式上存在不一致,因此尝试编写一个可移植到多个 C++ 版本,甚至可移植到 C++20 模式下构建的多个编译器上的视图适配器特别棘手。Boost.STLInterfaces 提供了一个类型 boost::stl_interfaces::range_adaptor_closure,它与 C++23 的 std::range_adaptor_closure 兼容,并且也适用于早期版本的 C++。Boost.STLInterfaces 还提供了帮助模板,使得编写具有 operator| 支持的视图适配器更加容易。

最令人畏惧的是编写一个符合标准中容器要求的类型或模板的任务。Boost.STLInterfaces 提供了另一个名为 sequence_container_interface 的模板,该模板大大减轻了实现和测试的负担。

[Note] 注意

提供了 iterator_interfacesequence_container_interface 的 C++20 版本(C++20 提供了 std::view_interface)。这些是使用 C++20 Concepts 的受约束模板。这些位于 boost::stl_interfaces::v2 命名空间中。还有一个 C++23 版本的 iterator_interface,它使用推导 this 而不是 CRTP。如果您不做任何操作,您将获得最新的 vN 命名空间内联,具体取决于您的编译器语言支持。

快速示例

这是该库的迭代器部分的一个例子。假设我们要创建一个随机访问迭代器,它表示一个任意长度的字符串,该字符串是通过重复一个较短的字符串构建的。我们称之为 repeated_chars_iterator。以下是它的实际应用:

repeated_chars_iterator first("foo", 3, 0); // 3 is the length of "foo", 0 is this iterator's position.
repeated_chars_iterator last("foo", 3, 7);  // Same as above, but now the iterator's position is 7.
std::string result;
std::copy(first, last, std::back_inserter(result));
assert(result == "foofoof");

标准库中没有能提供这种行为的东西,所以我们必须自己写。这个库旨在将我们从这样写(

struct repeated_chars_iterator
{
    using value_type = char;
    using difference_type = std::ptrdiff_t;
    using pointer = char const *;
    using reference = char const;
    using iterator_category = std::random_access_iterator_tag;

    constexpr repeated_chars_iterator() noexcept :
        first_(nullptr),
        size_(0),
        n_(0)
    {}
    constexpr repeated_chars_iterator(
        char const * first,
        difference_type size,
        difference_type n) noexcept :
        first_(first),
        size_(size),
        n_(n)
    {}

    constexpr reference operator*() const noexcept
    {
        return first_[n_ % size_];
    }

    constexpr value_type operator[](difference_type n) const noexcept
    {
        return first_[(n_ + n) % size_];
    }

    constexpr repeated_chars_iterator & operator++() noexcept
    {
        ++n_;
        return *this;
    }
    constexpr repeated_chars_iterator operator++(int)noexcept
    {
        repeated_chars_iterator retval = *this;
        ++*this;
        return retval;
    }
    constexpr repeated_chars_iterator & operator+=(difference_type n) noexcept
    {
        n_ += n;
        return *this;
    }

    constexpr repeated_chars_iterator & operator--() noexcept
    {
        --n_;
        return *this;
    }
    constexpr repeated_chars_iterator operator--(int)noexcept
    {
        repeated_chars_iterator retval = *this;
        --*this;
        return retval;
    }
    constexpr repeated_chars_iterator & operator-=(difference_type n) noexcept
    {
        n_ -= n;
        return *this;
    }

    friend constexpr bool operator==(
        repeated_chars_iterator lhs, repeated_chars_iterator rhs) noexcept
    {
        return lhs.first_ == rhs.first_ && lhs.n_ == rhs.n_;
    }
    friend constexpr bool operator!=(
        repeated_chars_iterator lhs, repeated_chars_iterator rhs) noexcept
    {
        return !(lhs == rhs);
    }
    friend constexpr bool operator<(
        repeated_chars_iterator lhs, repeated_chars_iterator rhs) noexcept
    {
        return lhs.first_ == rhs.first_ && lhs.n_ < rhs.n_;
    }
    friend constexpr bool operator<=(
        repeated_chars_iterator lhs, repeated_chars_iterator rhs) noexcept
    {
        return lhs == rhs || lhs < rhs;
    }
    friend constexpr bool operator>(
        repeated_chars_iterator lhs, repeated_chars_iterator rhs) noexcept
    {
        return rhs < lhs;
    }
    friend constexpr bool operator>=(
        repeated_chars_iterator lhs, repeated_chars_iterator rhs) noexcept
    {
        return rhs <= lhs;
    }

    friend constexpr repeated_chars_iterator
    operator+(repeated_chars_iterator lhs, difference_type rhs) noexcept
    {
        return lhs += rhs;
    }
    friend constexpr repeated_chars_iterator
    operator+(difference_type lhs, repeated_chars_iterator rhs) noexcept
    {
        return rhs += lhs;
    }
    friend constexpr repeated_chars_iterator
    operator-(repeated_chars_iterator lhs, difference_type rhs) noexcept
    {
        return lhs -= rhs;
    }
    friend constexpr difference_type operator-(
        repeated_chars_iterator lhs, repeated_chars_iterator rhs) noexcept
    {
        return lhs.n_ - rhs.n_;
    }

private:
    char const * first_;
    difference_type size_;
    difference_type n_;
};

(这有很多代码!)变成这样(

struct repeated_chars_iterator : boost::stl_interfaces::iterator_interface<
#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
                                     repeated_chars_iterator,
#endif
                                     std::random_access_iterator_tag,
                                     char,
                                     char>
{
    constexpr repeated_chars_iterator() noexcept :
        first_(nullptr),
        size_(0),
        n_(0)
    {}
    constexpr repeated_chars_iterator(
        char const * first, difference_type size, difference_type n) noexcept :
        first_(first),
        size_(size),
        n_(n)
    {}

    constexpr char operator*() const noexcept { return first_[n_ % size_]; }
    constexpr repeated_chars_iterator & operator+=(std::ptrdiff_t i) noexcept
    {
        n_ += i;
        return *this;
    }
    constexpr auto operator-(repeated_chars_iterator other) const noexcept
    {
        return n_ - other.n_;
    }

private:
    char const * first_;
    difference_type size_;
    difference_type n_;
};

这样就好多了。这两个 repeated_chars_iterator 的定义具有相同的语义和性能特征。第二个定义只是需要写更少的代码,并且编写第二个定义对初学者更友好。

[Note] 注意

Boost.STLInterfaces 的 iterator_interface 实现了模拟 C++20 迭代器 Concepts 的迭代器。


PrevUpHomeNext