考虑以下代码:
#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";
}
这是一个玩具示例,说明了我在真实代码中遇到的情况。我想简化实现,flatten(...)
以便在变体中有更多类型时不那么冗长。
基本上情况是,我有一个变体,其中包含一些简单的类型和一些我想做的事情。我需要执行的操作对于所有简单类型和所有仅移动类型都相同;但是,我想不出只使用两个访问函数来处理这两种情况(简单或仅移动)的方法。例如,这是非法的 C++,但说明了我想要做什么
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
);
}
过去,我通过使用自定义变体转换(类似于此处实现的转换)来处理此类情况,将其转换为仅包含需要相同处理的类型的变体,然后使用 lambda 将 auto 参数作为访客; 但是,这种转换在这种情况下不起作用,因为您不能复制 unique_ptrs 并且不能制作包含引用的变体。我想我可以编写一个函数来转换为指针的变体,但我想知道是否有更简单的方法。