如何在 C++ 中为自定义生成器实现重置功能?

How to implement a resetting function for custom generator in c++?

我创建了一个名为 zgenerator 的 class:

template <typename T>
class zgenerator {
  public:
    class promise_type;
    using handle = std::coroutine_handle<promise_type>;

  private:
    handle coro;
    explicit zgenerator(handle h) : coro{h} {}

  public:
    // default constructor
    zgenerator() noexcept : coro{nullptr} {}
    // move constructor and assignment
    zgenerator(zgenerator&& other) noexcept : coro{std::move(other.coro)} { }
    zgenerator& operator=(zgenerator&& other) noexcept {
        coro = std::move(other.coro);
        return *this;
    }
    // deleting copy constructor and assignment
    zgenerator(const zgenerator&) = delete;
    zgenerator& operator=(const zgenerator&) = delete;
    // destructor
    ~zgenerator() { if (coro) coro.destroy(); }

    // promise type
    class promise_type {
      public:
        using value_type = std::remove_reference_t<T>;
        using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
        using pointer_type = value_type*;

      private:
        pointer_type m_value;
        std::exception_ptr m_except;

      public:
        zgenerator get_return_object() noexcept {
            return zgenerator{handle::from_promise(*this)};
        }

        static auto get_return_object_on_allocation_failure() {
            return zgenerator{nullptr};
        }

        std::suspend_always initial_suspend() const noexcept { return {}; }
        std::suspend_always final_suspend() const noexcept { return {}; }

        // no 'co_await'
        template <typename T1> std::suspend_never await_transform(T1&&) = delete;

        std::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
            requires std::is_rvalue_reference_v<T>
        {
            m_value = std::addressof(value);
            return {};
        }

        std::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept {
            m_value = std::addressof(value);
            return {};
        }

        void unhandled_exception() {
            m_except = std::current_exception();
        }

        void rethrow_if_exception() {
            if (m_except)
                std::rethrow_exception(m_except);
        }

        void return_void() {}

        reference_type value() const noexcept {
            return static_cast<reference_type>(*m_value);
        }

    };

    // sentinel
    struct sentinel {};

    // iterator
    class iterator {
      private:
        handle m_coro;
      public:
        using value_type = typename promise_type::value_type;
        using reference  = typename promise_type::reference_type;
        using pointer    = typename promise_type::pointer_type;
        using difference_type = std::ptrdiff_t;
        using iterator_category = std::input_iterator_tag;

        iterator() noexcept : m_coro{nullptr} {}
        explicit iterator(handle other) noexcept : m_coro{other} {}

        friend bool operator==(const iterator& it, sentinel) noexcept {
            return !it.m_coro || it.m_coro.done();
        }

        friend bool operator==(sentinel s, const iterator& it) noexcept {
            return (it == s);
        }

        iterator& operator++() {
            m_coro.resume();
            if (m_coro.done()) {
                m_coro.promise().rethrow_if_exception();
            }
            return *this;
        }

        void operator++(int) {
            (void) this->operator++();
        }

        reference operator*() const noexcept {
            return m_coro.promise().value();
        }

        pointer operator->() const noexcept {
            return std::addressof(this->operator*());
        }
    };

    // iterators
    iterator begin() {
        if (coro) {
            coro.resume();
            if (coro.done()) {
                coro.promise().rethrow_if_exception();
            }
        }
        return iterator{coro};
    }

    sentinel end() noexcept {
        return sentinel{};
    }

    void swap(zgenerator& other) noexcept {
        using std::swap;
        swap(coro, other.coro);
    }

    // manual yielding
    T value() { return coro.promise().value(); }
    bool next() {
        if (coro) {
            coro.resume();
            return !coro.done();
        } else
            return false;
    }
    bool is_done() { return coro.done(); }
};

这是应用程序:

auto generate_num = [] -> zgenerator<int> {
    co_yield 1;
    co_yield 3;
    co_yield 10;
    co_yield -1;
};
auto it = generate_num();

// Used in for-loop:
for (const auto& i : it)
    std::cout << i << ' ';
std::cout << '\n';

输出:

1 3 10 -1

我想将对象重置回第一个状态。当我试图从 it 重新分配一个新的生成器对象时,它抛出一个运行时错误:

it = generate_num();

期望:

// reset:
it.reset();
it.next();
while (!it.is_done()) {
    std::cout << it.value() << ' ';
    it.next();
}
// now printing twice if we include this.

如何在我的发电机中实现重置功能?

您的移动构造函数和赋值需要确保 other.coro 不会仍然指向它以前的位置,否则您会双重破坏它。

zgenerator(zgenerator&& other) noexcept : coro{std::exchange(other.coro, {})} { }
zgenerator& operator=(zgenerator&& other) noexcept {
    std::swap(coro, other.coro);
    return *this;
}