Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concatenate different types using ranges-v3

Tags:

c++

range-v3

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

like image 662
jjcasmar Avatar asked May 13 '26 14:05

jjcasmar


2 Answers

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

like image 93
康桓瑋 Avatar answered May 16 '26 03:05

康桓瑋


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));
like image 45
Deduplicator Avatar answered May 16 '26 04:05

Deduplicator



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!