I have two different objects:
struct TypeA {
std::size_t no;
std::string data;
std::string data2;
};
struct TypeB {
std::size_t no;
std::string data;
std::string data2;
std::string data3;
};
They are stored in a std::vector
with std::variant
std::vector<std::variant< TypeA, TypeB>> ab;
Now i want to remove all elements were the member no = 0
.
Without the std::variant
with the vector containing only TypeA
I would do it like this:
ab.erase(std::remove_if(ab.begin(), ab.end(),
[](const TypeA& a) { return a.no == 0; }), ab.end());
But how to incorporate the std::variant
? I tried to come up with something with std::visit
but i cannot ad it in the predicate of std::remove_if
or can I?
remove() is an inbuilt function in C++ STL which is declared in header file. remove() is used to remove any specific value/element from the list container. It takes the value which is passed as a parameter and removes all the elements with that value from the list container.
The C++ vector has many member functions. Two of these member functions are erase() and pop_back(). pop_back() removes the last element from the vector. In order to remove all the elements from the vector, using pop_back(), the pop_back() function has to be repeated the number of times there are elements.
The recommended way is to use the remove_if and erase pattern. Note that std::vector has two versions of erase(). The one used in this thread takes two arguments and removes a range of elements.
Yes, std::visit
can help. The functor passed to visit
just needs to be able to accept each type of the variant
, and the easiest way to do that is with a generic lambda:
ab.erase(
std::remove_if(
ab.begin(),
ab.end(),
[](const auto &v) {
return std::visit(
[](const auto &obj) { return obj.no == 0; },
v);
}),
ab.end());
Here the type of v
for the outer lambda is always used as const std::variant<TypeA, TypeB>&
, and auto
is just more convenient than typing out std::variant<TypeA, TypeB>
. But for the inner lambda, it's important that the lambda is generic, because visit
will instantiate its template operator()
with both TypeA
and TypeB
.
If you want to access the "same" data member of different types, then these types need to be subclasses of a common polymorphic base class defining this data member.
In your case, however, where TypeA
and TypeB
are not related, you'll have to make a type-safe access to the respective data member. The solution provided by @aschepler shows this in a generic way using std::visit
functor; the following solution is without std::visit
(hence not that elegant, but still working):
ab.erase(std::remove_if(ab.begin(), ab.end(),
[](const std::variant< TypeA, TypeB>& v) {
int no;
if (v.index()==0) {
no = std::get<0>(v).no;
} else {
no = std::get<1>(v).no;
}
return no==0;
}), ab.end());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With