Home > OS >  Concatenate different types using ranges-v3
Concatenate different types using ranges-v3

Time:07-12

I would like to know if its possible to concatenate two vectors holding different type of objects, such that I can then iterate on the concatenation and call a common interface.

Something like this:

    std::vector<A> as;
    as.resize(3);

    std::vector<B> bs;
    bs.resize(4);

    for (const auto &v : ranges::views::concat(as, bs))
    {
        foo(v);
    }

You can find a full example here https://godbolt.org/z/nr5hhWMxj

CodePudding user response:

You can use views::transform to convert the elements of both containers to std::variant for type erasure.

auto to_variant = ranges::views::transform(
                    [](auto x) { return std::variant<A, B>(std::move(x)); });
for (const auto& v : ranges::views::concat(as | to_variant, bs | to_variant)) {
  std::visit([](const auto& v) { foo(v); }, v);
}

Demo

CodePudding user response:

The problem with that is simple:

What is the common interface?

If they have a common type, like one is descended from the other, views::concat() is intelligent enough to go there, and you still have dynamic dispatch for virtual member functions.

If they are both descended from the same base (but neither is the base), you would be able to get the above by using views::transform().

Otherwise, you would need to views::transform() them into a proxy-object which can reference either, and dispatches the members you care about appropriately.
While std::variant cannot be it, unless you are fine with a copy, std::variant storing std::reference_wrappers may work.

auto map = ranges::views::transform([](auto& x) {
    return std::variant<std::reference_wrapper<A>, std::reference_wrapper<B>>(std::ref(x)); });
for (const auto& v : ranges::views::concat(as | map, bs | map))
    std::visit([](auto x) {
        auto& v = x.get();
        foo(v);
    }, v);

Alternative

Use static_for_each:

template <class F, class T>
constexpr void static_for_each(F&& f, T&& t) {
    std::apply([&](auto&&... x){
        ((f(std::forward<decltype(x)>(x)), void()), ...);
    }, t);
}

static_for_each([&](auto&& c){
    for (auto&& v : c)
        foo(v);
}, std::forward_as_tuple(as, bs));
  • Related