You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取可遍历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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 12:45:54