为何std::move与std::list结合自定义视图会引发无限递归?
问题分析与解答
问题现象
自定义ContainerView视图适配std::list时,若将std::move(my_list)传入std::views::all构造视图,迭代时会无限重复打印元素;但换成std::vector则正常,直接迭代std::move后的std::list也无异常。
核心原因
问题出在自定义视图的实现违反了C++20范围视图的语义约束:视图不应存储构造时的迭代器,而应在需要时从内部持有的范围动态获取迭代器。
你的ContainerView在构造时先获取输入视图的迭代器并存储为begin_和end_,再将输入视图移动到内部成员range_。这种做法存在致命问题:
- 当输入视图被移动后,存储的迭代器可能不再指向
range_当前持有的范围。对于std::vector,这属于未定义行为,只是碰巧在你的实现中能正常工作; - 对于
std::list,虽然移动容器后迭代器本身仍指向原元素,但由于range_是移动后的新容器,存储的end_迭代器与range_实际的尾后迭代器在比较时可能永远不相等(部分实现中std::list的迭代器比较会关联容器的内部状态),导致循环无法终止,陷入无限打印。
修正方案
修改ContainerView,仅存储内部的Range对象,在begin()和end()成员函数中动态获取当前范围的迭代器:
#include <concepts> #include <iostream> #include <list> #include <ranges> #include <vector> template <std::ranges::input_range Range> requires std::ranges::view<Range> class ContainerView : public std::ranges::view_interface<ContainerView<Range>> { private: Range range_ {}; public: constexpr ContainerView() = default; constexpr ContainerView(Range r) : range_(std::move(r)) { } constexpr auto begin() const { return std::begin(range_); } constexpr auto end() const { return std::end(range_); } }; int main() { std::list my_list { 1, 2, 3 }; auto my_container_view { ContainerView(std::views::all(std::move(my_list))) }; for (auto const& c : my_container_view) std::cout << c << '\n'; }
这样修改后,每次迭代都会从当前的range_获取正确的迭代器,无论内部范围是否被移动,都能保证迭代行为符合预期。
内容的提问来源于stack exchange,提问作者Abel Sen
相关产品推荐
相关产品推荐

