C++协程生成器遍历string_view是否安全?临时对象生命周期疑问
你的代码存在悬空引用风险,不安全
首先直接给结论:这段代码会触发未定义行为,因为string_view引用的临时std::string会在协程挂起前就被销毁,导致后续co_yield访问悬空引用。
具体原因拆解:
- 临时对象生命周期规则:
get_string()返回的临时std::string,其创建它的完整表达式是iterate_string(get_string())。按照C++标准,临时对象会在完整表达式结束时销毁——也就是iterate_string函数调用完成(协程创建帧并执行到第一个co_yield挂起之前),这个临时string就已经被销毁了,此时string_view sv就变成了悬空引用。 - 范围for的生命周期延长不适用这里:范围for确实会延长某些临时容器的生命周期,但只针对迭代器所属的容器对象本身(比如如果直接写
for (auto c : get_string()),临时string会被延长到循环结束)。但你的场景里,范围for迭代的是MyGenerator<char>对象,这个生成器只持有悬空的string_view,范围for不会为那个已经销毁的临时string续命。 - 和lambda协程捕获问题的区别:lambda协程是临时lambda对象在首个挂起点后销毁,导致捕获的引用失效;而你的场景是临时string先销毁,导致
string_view悬空——本质都是「引用/视图指向的对象生命周期没覆盖协程执行全程」,只是触发时机不同。 - 对应CP.53规则:这条规则的核心是避免协程依赖外部对象的生命周期,哪怕是
string_view这种值传递的视图类型,因为它本质还是引用语义,只要指向的对象在协程挂起期间销毁,就会出问题。
修复方案
有两种常见的安全写法:
- 让协程持有字符串副本:把参数改成
std::string值传递,这样协程帧会保存字符串的副本,string_view可以安全引用这个副本:
std::string get_string(); MyGenerator<char> iterate_string(std::string s) { std::string_view sv = s; for (auto c : sv) co_yield c; } void do_something() { for (auto c : iterate_string(get_string())) std::cout << c; }
- 确保临时字符串生命周期覆盖协程全程:在范围for外先存储临时字符串,让它的生命周期覆盖整个循环(也就是协程的所有挂起和恢复过程):
void do_something() { auto str = get_string(); for (auto c : iterate_string(str)) std::cout << c; }
内容的提问来源于stack exchange,提问作者Dix
相关产品推荐
相关产品推荐

