My question consists of two parts:
First part. Am I correct that the following task couldn’t be solved in C++ even with recent innovations in templates?
The goal is to virtually (which means no real concatenation should occur) sequentially concatenate two C++ std::vectors of objects of different types for the time of function call.
My understanding that this is not doable since it is impossible at compile time to generate the code that will handle objects of different types (in the function) without usage of dynamic polymorphism.
Details for both parts follow below. Now is the second part. What is the simplest way to solve this task in case vectors contain the elements of the same type?
I have objects of some classes in different vectors and want some function to process them as whole. I don’t want to use virtual functions, dynamic memory allocation neither put all objects of different types (pointers to them) in the same container, since in most of code they treated differently and I don’t won’t to pay extra price neither to make complicated interfaces for my classes.
The code below is taken from the answer from Evg to satisfy request from AlanBirtles in comment below for minimal reproducible example.
#include <vector>
#include <iostream>
struct A {
void foo() { std::cout << "Hey, I'm A!\n"; }
};
struct B {
void foo() { std::cout << "Hey, I'm B!\n"; }
};
void bar(???) {
}
int main() {
std::vector<A> va(2);
std::vector<B> vb(3);
bar(???);
return 0;
}
Of course, I can process all these vectors one by one, but I have many of them and this would lead to code duplication and risks of forgetting to update such code when a new type is added.
I am sure that for the elements of the same type in vectors this could be done by a new container class that wraps all these containers and provides iterators for them, but is there a simpler way to make this on the fly?
>Solution :
You could do some metaprogramming using variadic parameter packs and fold expressions. For example:
template<class... Ts>
void bar(Ts&... vecs) {
const auto call_foo_for_each = [](auto& vec) {
for (auto& v : vec)
v.foo();
};
(call_foo_for_each(vecs), ...);
}
void caller() {
bar(va, vb /*, ... */);
}
If you want to pass them as a single argument, you could pack them into a tuple (of references):
template<class... Ts>
void bar(std::tuple<Ts...> tuple) {
const auto call_foo_for_each = [](auto& vec ) {
for (auto& v : vec)
v.foo();
};
std::apply([&](auto&... vecs) {
(call_foo_for_each(vecs), ...);
}, tuple);
}
void caller() {
bar(std::make_tuple(std::ref(va), std::ref(vb) /*, ... */));
}