如何获取可遍历variant中容器的多态std::ranges?
实现支持Variant的多态Range(可组合)
你的核心问题是需要将Variant中不同类型的集合统一为单一类型的Range,使其能与std::ranges的各种视图(如transform、filter)组合。下面提供两种可行方案:
方案一:C++23 协程Generator方案
利用C++23引入的std::generator,可以轻松实现延迟生成元素的多态Range,无需手动处理类型擦除。Generator本身就是符合Range概念的类型,天然支持与其他视图组合。
实现代码
#include <generator> #include <variant> #include <set> #include <ranges> #include <tuple> #include <vector> struct Interval { size_t start; size_t end; }; struct Single { size_t value; }; using VariantSet = std::variant<std::set<size_t>, Interval, Single>; using Mapping = std::vector<std::tuple<VariantSet, size_t>>; std::generator<size_t> getMappedElements(const Mapping& mapping, size_t interestingId) { // 遍历所有映射项,筛选出目标ID对应的集合 for (const auto& [set, id] : mapping) { if (id != interestingId) continue; // 用visit遍历Variant中的不同集合类型,逐个yield元素 co_await std::visit([&](const auto& s) -> std::generator<size_t> { if constexpr (std::same_as<decltype(s), const std::set<size_t>&>) { for (auto elem : s) co_yield elem; } else if constexpr (std::same_as<decltype(s), const Interval&>) { for (auto elem = s.start; elem <= s.end; ++elem) co_yield elem; } else if constexpr (std::same_as<decltype(s), const Single&>) { co_yield s.value; } }, set); } } // 使用示例 void do_something_with_the_element(size_t) {} int main() { Mapping mapping = { {Single{42}, 1}, {Interval{10, 15}, 1}, {std::set<size_t>{20, 25, 30}, 1} }; // 支持与transform等视图组合 for (auto elem : getMappedElements(mapping, 1) | std::views::transform([](size_t x) { return x * 2; })) { do_something_with_the_element(elem); } }
优势
- 代码简洁,无需手动实现类型擦除逻辑
- 延迟生成元素,内存占用低,适合百万级元素的场景
- 天然支持与
std::ranges的各种视图组合
方案二:C++20 类型擦除Range方案
如果无法升级到C++23,可以手动实现类型擦除的Input迭代器和Range,将不同类型的Range统一为单一类型,满足std::ranges的组合要求。
实现代码
#include <memory> #include <iterator> #include <variant> #include <set> #include <ranges> #include <tuple> #include <vector> struct Interval { size_t start; size_t end; }; struct Single { size_t value; }; using VariantSet = std::variant<std::set<size_t>, Interval, Single>; using Mapping = std::vector<std::tuple<VariantSet, size_t>>; // 类型擦除的Input迭代器,包装任意返回size_t的Input迭代器 class AnySizeTInputIterator { public: using value_type = size_t; using difference_type = std::ptrdiff_t; using pointer = const size_t*; using reference = const size_t&; using iterator_category = std::input_iterator_tag; template <std::input_iterator Iter> AnySizeTInputIterator(Iter it) : impl_(std::make_unique<Impl<Iter>>(std::move(it))) {} AnySizeTInputIterator(const AnySizeTInputIterator& other) : impl_(other.impl_->clone()) {} AnySizeTInputIterator& operator=(const AnySizeTInputIterator& other) { if (this != &other) impl_ = other.impl_->clone(); return *this; } AnySizeTInputIterator(AnySizeTInputIterator&&) noexcept = default; AnySizeTInputIterator& operator=(AnySizeTInputIterator&&) noexcept = default; reference operator*() const { return impl_->deref(); } pointer operator->() const { return std::addressof(impl_->deref()); } AnySizeTInputIterator& operator++() { impl_->increment(); return *this; } AnySizeTInputIterator operator++(int) { auto copy = *this; ++*this; return copy; } bool operator==(const AnySizeTInputIterator& other) const { return impl_->equals(*other.impl_); } private: struct ImplBase { virtual ~ImplBase() = default; virtual std::unique_ptr<ImplBase> clone() const = 0; virtual reference deref() const = 0; virtual void increment() = 0; virtual bool equals(const ImplBase& other) const = 0; }; template <std::input_iterator Iter> struct Impl : ImplBase { explicit Impl(Iter it) : it_(std::move(it)) {} std::unique_ptr<ImplBase> clone() const override { return std::make_unique<Impl>(it_); } reference deref() const override { return *it_; } void increment() override { ++it_; } bool equals(const ImplBase& other) const override { const auto* other_impl = dynamic_cast<const Impl*>(&other); return other_impl && it_ == other_impl->it_; } Iter it_; }; std::unique_ptr<ImplBase> impl_; }; // 类型擦除的Input Range class AnySizeTInputRange { public: template <std::ranges::input_range R> AnySizeTInputRange(R&& r) : begin_(std::ranges::begin(r)), end_(std::ranges::end(r)) {} AnySizeTInputIterator begin() const { return begin_; } AnySizeTInputIterator end() const { return end_; } private: AnySizeTInputIterator begin_; AnySizeTInputIterator end_; }; // 将VariantSet转换为类型擦除的Range AnySizeTInputRange to_range(const VariantSet& vs) { return std::visit([](const auto& s) -> AnySizeTInputRange { if constexpr (std::same_as<decltype(s), const std::set<size_t>&>) { return s; } else if constexpr (std::same_as<decltype(s), const Interval&>) { return std::views::iota(s.start, s.end + 1); // iota是左闭右开,需+1覆盖end } else if constexpr (std::same_as<decltype(s), const Single&>) { return std::views::single(s.value); } }, vs); } // 生成可组合的Range auto getMappedElements(const Mapping& mapping, size_t interestingId) { return mapping | std::views::filter([interestingId](const auto& tuple) { return std::get<1>(tuple) == interestingId; }) | std::views::transform([](const auto& tuple) { return to_range(std::get<0>(tuple)); }) | std::views::join; } // 使用示例 void do_something_with_the_element(size_t) {} int main() { Mapping mapping = { {Single{42}, 1}, {Interval{10, 15}, 1}, {std::set<size_t>{20, 25, 30}, 1} }; // 支持与transform等视图组合 for (auto elem : getMappedElements(mapping, 1) | std::views::transform([](size_t x) { return x * 2; })) { do_something_with_the_element(elem); } }
优势
- 兼容C++20及以上版本
- 同样支持延迟生成,内存友好
- 类型擦除的迭代器可复用在其他类似场景
内容的提问来源于stack exchange,提问作者Carlos
相关产品推荐
相关产品推荐

