Consider the following code:
#include <iostream>
#include <variant>
#include <memory>
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...)->overloaded<Ts...>;
struct foo {
int f;
foo(int n) : f(n) {}
};
struct bar {
std::string b;
};
using unflattened_variant = std::variant<int, std::string, std::unique_ptr<foo>, std::unique_ptr<bar>>;
using flattened_variant = std::variant<int, std::string, foo, bar>;
flattened_variant flatten(const unflattened_variant& v) {
return std::visit(
overloaded{
[](int v) -> flattened_variant {
return v;
},
[](const std::string& s) -> flattened_variant {
return s;
},
[](const std::unique_ptr<foo>& f) -> flattened_variant {
return *f;
},
[](const std::unique_ptr<bar>& b) -> flattened_variant {
return *b;
},
},
v
);
}
int main()
{
unflattened_variant uv{ std::make_unique<foo>(42) };
auto fv = flatten(uv);
std::cout << std::get<foo>(fv).f << "\n";
}
This is a toy example that illustrates a situation I am running into in real code. I want to simplify the implementation of flatten(...)
such that it is less verbose when there are more types in the variant.
Basically the situation is, I have a variant that contains some simple types and some move-only types that I would like to do something with. The operation that I need to perform is the same for all simple types and the same for all the move-only types; however, I can't think of a way of dealing with the two cases (simple or move-only) using only two visiting functions. e.g. this is illegal C++ but illustrates what I want to do
flattened_variant flatten(const unflattened_variant& v) {
return std::visit(
overloaded{
[](const std::unique_ptr<auto>& u_ptr) -> flattened_variant {
return *u_ptr;
},
[](auto simple_value) -> flattened_variant {
return simple_value;
},
},
v
);
}
I have dealt with situations like this in the past by using a custom variant cast, similar to the one implemented here, to cast to a variant containing just those types that need to be handled the same and then using a lambda taking an auto parameter as the visitor; however, such a cast would not work in this case because you can't copy unique_ptrs and you can't make a variant containing references. I suppose I could write a function that will cast to a variant of pointers but am wondering if there is an easier way.