我正在尝试学习和实现C++20协程,但我遇到了一个错误。
生成器类:
template<class ReturnType = void>
class enumerable
{
public:
class promise_type;
using handle_type = std::coroutine_handle<promise_type>;
class promise_type
{
public:
ReturnType current_value{};
auto get_return_object()
{
return enumerable{ handle_type::from_promise(*this) };
}
auto initial_suspend()
{
return std::suspend_always{};
}
auto final_suspend() noexcept
{
return std::suspend_always();
}
void unhandled_exception()
{
// TODO:
}
void return_void()
{
}
auto yield_value(ReturnType& value) noexcept
{
current_value = std::move(value);
return std::suspend_always{};
}
auto yield_value(ReturnType&& value) noexcept
{
return yield_value(value);
}
};
class iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = ReturnType;
using pointer = ReturnType*;
using reference = ReturnType&;
private:
handle_type handle;
public:
iterator(handle_type handle)
: handle(handle)
{
}
reference operator*() const
{
return this->handle.promise().current_value;
}
pointer operator->()
{
return &this->handle.promise().current_value;
}
iterator& operator++()
{
this->handle.resume();
return *this;
}
friend bool operator==(const iterator& it, std::default_sentinel_t s) noexcept
{
return !it.handle || it.handle.done();
}
friend bool operator!=(const iterator& it, std::default_sentinel_t s) noexcept
{
return !(it == s);
}
friend bool operator==(std::default_sentinel_t s, const iterator& it) noexcept
{
return (it == s);
}
friend bool operator!=(std::default_sentinel_t s, const iterator& it) noexcept
{
return it != s;
}
};
handle_type handle;
enumerable() = delete;
enumerable(handle_type h)
: handle(h)
{
std::cout << "enumerable constructed: " << this << " : " << this->handle.address() << '\n';
};
iterator begin()
{
this->handle.resume();
return iterator(this->handle);
}
std::default_sentinel_t end()
{
return {};
}
//Filters a sequence of values based on a predicate.
template<class Predicate>
enumerable<ReturnType> where(Predicate&& pred)
{
std::cout << "where: " << this << " : " << this->handle.address() << '\n';
for (auto& i : *this)
{
if(pred(i))
co_yield i;
}
}
~enumerable()
{
std::cout << "enumerable destructed: " << this << " : " << this->handle.address() << '\n';
}
};测试代码:
enumerable<int> numbers()
{
co_yield 1;
co_yield 2;
co_yield 3;
co_yield 4;
}
enumerable<int> filtered_numbers()
{
return numbers().where([](int i) { return true; });
}// Crashes
int main()
{
for (auto& i : filtered_numbers())
{
std::cout << "value: " << i << '\n';
}
return 0;
}输出:
enumerable constructed: 000000FF0550F560:000002959E3B5290
enumerable constructed: 000000FF0550F5D8:000002959E3B6470
destructed: 000000FF0550F560 : 000002959E3B5290
where: 000000FF0550F560 : 000000FF0550F640//Works, despite "this" inside "where" still being destructed before use, can be observed with the couts.
int main()
{
for(auto i : numbers().where([](int i) { return true; }))
{
std::cout << "value: " << i << '\n';
}
return 0;
}输出:
enumerable constructed: 000000C9EDD2FD78:000001DADD1A61D0
enumerable constructed: 000000C9EDD2FD28:000001DADD1A73B0
destructed: 000000C9EDD2FD78 : 000001DADD1A61D0
where: 000000C9EDD2FD78 : 000001DADD1A61D0
value: 1
value: 2
value: 3
value: 4
destructed: 000000C9EDD2FD28 : 000001DADD1A73B0有人能解释一下这里发生了什么吗?如果可能的话,我想想出一个解决办法,如果我们在promise_type的" initial_suspend“中返回"std::suspend_never”,就不会发生崩溃,但是在initial_suspend中挂起对于生成器来说不是理想的行为。
发布于 2021-07-07 05:34:45
这一点:
返回数字().where(int i{
true;});
创建一个临时(numbers()),然后在协程(循环中使用的*this )中存储对该临时的引用,然后临时就消失了。
那可不好。如果你想做协程的链接,链中的每一步都需要是某人堆栈上的一个对象。where可以是通过值接受enumerable的非成员函数。这将允许where协程保留enumerable的存在。
发布于 2021-07-07 22:13:11
//Filters a sequence of values based on a predicate.
template<class Predicate>
enumerable<ReturnType> where(Predicate pred)&
{
std::cout << "where: " << this << " : " << this->handle.address() << '\n';
for (auto& i : *this)
{
if(pred(i))
co_yield i;
}
}
//Captures *this as well as above.
template<class Predicate>
enumerable<ReturnType> where(Predicate pred)&&
{
auto self=std::move(*this);
std::cout << "where: " << this << " : " << this->handle.address() << '\n';
for (auto& i : self)
{
if(pred(i))
co_yield i;
}
}有两个变化。
Predicate by value,以避免悬空引用问题。&&重载,它复制*this (好吧,从其中移动)并将其存储在协程中。这仍然不起作用。
发生的第一件事是,在运行任何代码之前,我们的协程被挂起。所以,当我们第一次尝试获取一个值时,auto self=std::move(*this)的副本就会发生。
我们可以通过几种方式来解决这个问题。其中之一是跳转到一个免费函数,并让它复制enumerable<int>
template<class Predicate>
friend enumerable<ReturnType> where( enumerable<ReturnType> self, Predicate pred ) {
for (auto& i: self)
if (pred(i))
co_yield i;
}
//Filters a sequence of values based on a predicate.
template<class Predicate>
enumerable<ReturnType> where(Predicate pred)&
{
return where( *this, std::move(pred) );
}
template<class Predicate>
enumerable<ReturnType> where(Predicate pred)&&
{
return where( std::move(*this), std::move(pred) );
}第二种方法是修改enumerable<ReturnType>以支持设置阶段。
struct init_done {};
auto initial_suspend() {
return std::suspend_never{};
}
auto yield_value(init_done) noexcept {
return std::suspend_always{};
}并在设置完成后将返回函数的enumerable<int>修改为第一个co_yield init_done{};。
我们将在numbers()协程的第一行执行此操作,并且在将*this复制到where()协程中的局部变量self之后。
这可能是最简单的:
template<class F>
friend
enumerable<ReturnType> where2(enumerable<ReturnType> self, F f )
{
for (auto i : self.where(std::move(f)))
co_yield i;
}
template<class F>
enumerable<ReturnType> where(F f)&&
{
return where2(std::move(*this), std::move(f));
}
template<class F>
enumerable<ReturnType> where(F f)&
{
for (auto i : *this)
{
if (f(i))
co_yield i;
}
}https://stackoverflow.com/questions/68277372
复制相似问题