如何在C++中实现输入迭代器的"dereference and post-increment"?

How to implement "dereference and post-increment" for input iterator in C++?

本文关键字:dereference and post-increment 迭代器 输入 C++ 实现      更新时间:2023-10-16

InputIterator 的要求*i++包括等效表达式为

value_type x = *i;
++i;
return x;

如何在不实现标准的后增量i++返回非 void 值的情况下声明这样的运算符(输入迭代器不需要这样做(?

您可以使用代理来增加帖子:

#include <iostream>
class input_iterator
{
    private:
    class post_increment_proxy
    {
        public:
        post_increment_proxy(int value) : value(value) {}
        int operator * () const { return value; }
        private:
        int value;
    };
    public:
    post_increment_proxy operator ++ (int) {
        post_increment_proxy result{value};
        ++value;
        return result;
    }
    private:
    int value = 0;
};

int main() {
    input_iterator i;
    std::cout << *i++ << 'n';
    std::cout << *i++ << 'n';
    std::cout << *i++ << 'n';
}

首先,迭代器是可复制的,即使在 InputIterator 的情况下,副本的行为更像是一个移动(具体来说,在你增加迭代器的任何一个副本之后,你不应该取消引用它的任何其他副本(。

尽管如此,复制迭代器应该没有任何问题 - 事实上,大多数库(以及许多其他代码(都假设迭代器是"轻量级"对象;复制它们很便宜,所以(对于一个明显的例子(它们通常是按值传递的,而不是通过引用传递的。

因此,稍微简化的流迭代器可能如下所示:

template <class T>
class istream_iterator {
    std::istream *is;
    T data;
public:
    istream_iterator(std::istream &is) : is(&is) { ++(*this); }
    istream_iterator() : is(nullptr) { }
    istream_iterator &operator++() { (*is) >> data; return *this; }
    // So here's the post-increment: it just saves a copy of itself, then
    // reads the next item (which increments the iterator), and finally
    // returns the copied object, which will return the previously-read item
    // from the stream when/if dereferenced.
    istream_iterator operator++(int) { 
        // Note: this uses the compiler-generated copy constructor. Assuming
        // a `T` is copy-constructible, this works fine--other than a T,
        // we're only copying a pointer.
        istream_iterator temp = *this; 
        (*is) >> data; 
        return temp; 
    }
    T const &operator*() const { return data; }
    bool operator !=(istream_iterator &end) { return (*is).good(); }
    bool operator ==(istream_iterator &end) { return !(*is).good(); }
};
这在

几个相当小的点上"作弊"——例如,两个默认构造的迭代器应该相互比较,这不会费心实现(而且几乎没有人使用或关心(。在正常使用中,从流创建一个迭代器,并默认构造另一个迭代器。当且仅当第一个到达输入流的末尾(或由于某种原因读取失败(时,两者之间的比较应返回 true。同样,这省略了实现operator->,以及标准迭代器类型(value_type、traits_type、istream_type等(所需的一些typedef。不过,这些都与手头的问题无关(所有这些都是添加所需代码的问题,而不是对已经存在的代码进行任何实质性更改(。

代码的快速演示可能如下所示:

int main() {
    istream_iterator<char> i(std::cin), end;
    while (i != end)
        std::cout << *i++;
}

这会将字符从标准输入复制到标准输出,跳过空格,因为默认情况下operator>>跳过空格(但如果需要,您可以使用noskipws消除空格(。

我也曾经遇到过这种情况。IMO,这个要求毫无意义。这是输入迭代器实现者的负担,并且该行为可以在迭代器的用户方面非常轻松地实现。

我的看法是这样的:

// -- inputitproxy.hpp ------
template <typename It, bool backward>
class increment_decrement_proxy
{
    It& it;
    bool incremented = false;
    void push()
    {
        if (!incremented)
        {
            if constexpr (backward) --it; else ++it;
        }
    }
public:
    increment_decrement_proxy() = delete;
    increment_decrement_proxy(const increment_decrement_proxy&) = delete;
    increment_decrement_proxy(increment_decrement_proxy&&) = delete;
    increment_decrement_proxy(It& it) : it{it} {}
    auto operator*() -> decltype(*it)
    {
        decltype(auto) result = *it;
        push();
        incremented = true;
        return result;
    }
    ~increment_decrement_proxy() { push(); }
    // ~increment_decrement_proxy() && { push(); }
    // ~increment_decrement_proxy() & = delete;
};
template <typename It>
using increment_proxy = increment_decrement_proxy<It, 0>;
template <typename It>
using decrement_proxy = increment_decrement_proxy<It, 1>;

你像这样使用它:

// -- inputit.hpp -----------
template <typename T>
class input_it
{
    T* ptr;
public:
    // boilerplate
    input_it(T* ptr) : ptr{ptr} {}
    T& operator*() const { return *ptr; }
    T* operator->() const { return ptr; }
    input_it& operator++() { ++ptr; return *this; }
    input_it& operator--() { --ptr; return *this; }
    // actual usage:
    auto operator++(int) { return increment_proxy{*this}; }
    auto operator--(int) { return decrement_proxy{*this}; }
};

一个工作示例:

#include "inputit.hpp"
#include <iostream>
struct no_copy
{
    int x;
    explicit no_copy(int i) : x(i) {}
    no_copy(const no_copy&) = delete;
};
int main()
{
    no_copy stuff[] { no_copy{1}, no_copy{2}, no_copy{3} };
    input_it it { &stuff[0] };
    std::cout << it->x << std::endl; // prints 1
    (void)it++;
    // Note: an input iterator is not supposed to be used like that.
    //auto it2 = it++; // compiles, but does not actually increment
    std::cout << it->x << std::endl; // prints 2
    std::cout << (*it++).x << std::endl; // prints 2 again
    std::cout << it->x << std::endl; // prints 3
}

两个注释掉的析构函数不起作用,因为析构函数上不允许引用注释。如果这样做有效,则初始化it2的行将无法编译。

你可以在这里尝试一下。