如何实现自动遍历std::variant获取各备选类型同名成员的通用函数?
可以实现,使用std::visit配合泛型lambda即可实现无感知适配新增备选类型的效果,C++20及以上标准可支持constexpr,也可扩展支持自动匹配不同成员名的场景。
固定取同名成员的通用实现
如果所有std::variant的备选类型都包含同名的x成员,使用下面的实现即可,后续给variant新增备选类型无需修改任何代码:
#include <variant> #include <utility> // 通用实现,兼容所有包含x成员的variant,支持左值/右值、const/非const版本 template<typename Variant> constexpr decltype(auto) getX(Variant&& variant) { return std::visit( [](auto&& obj) -> decltype(auto) { return obj.x; }, std::forward<Variant>(variant) ); }
调用方式和普通函数完全一致:
std::variant<A, B, C> v = B{}; int val = getX(v); // 自动返回B类型实例的x值1
适配不同成员名的场景
如果不同备选类型的目标成员名不同,可根据需求选择两种实现方案:
方案1:手动映射不同类型的成员
使用C++17即可支持的overloaded惯用法,明确指定不同类型的成员访问逻辑,新增类型只需新增对应lambda分支即可,比原有if分支逻辑更简洁:
// 重载lambda辅助工具,C++17可用 template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; // 自定义不同类型的成员访问逻辑 template<typename Variant> constexpr int get_val(Variant&& variant) { return std::visit(overloaded{ [](const A& a) { return a.x; }, // A类型取x成员 [](const B& b) { return b.y; }, // B类型取y成员 [](const C& c) { return c.z; }, // C类型取z成员 // 新增备选类型只需要在此处添加对应lambda即可 }, std::forward<Variant>(variant)); }
方案2:自动匹配存在的成员
如果需要完全自动化匹配当前类型可访问的成员,使用C++20的requires表达式做编译期分支判断,只要类型包含指定优先级的成员即可自动识别,新增类型无需修改代码:
template<typename Variant> constexpr int get_auto_val(Variant&& variant) { return std::visit([](auto&& obj) -> int { // 按优先级判断成员是否存在,匹配到第一个存在的成员就返回 if constexpr (requires { obj.x; }) { return obj.x; } else if constexpr (requires { obj.y; }) { return obj.y; } else if constexpr (requires { obj.z; }) { return obj.z; } else { // 没有匹配到任何成员时编译期报错 static_assert(false, "当前类型无符合要求的可访问成员"); } }, std::forward<Variant>(variant)); }
注意事项
- 访问的类成员需要对函数可见,如果成员为类私有属性,需要将访问函数声明为对应类的友元
constexpr特性需要C20及以上编译标准支持,C17可正常使用运行时版本的逻辑
内容的提问来源于stack exchange,提问作者Poperton
相关产品推荐
相关产品推荐

