如何将std::variant中的值提取为超类A*类型?
问题描述
现有类定义如下:
class A { public: virtual std::string to_string() = 0; }; class B : public A { public: std::string to_string(){ return "B"; } }; class C : public A { public: std::string to_string(){ return "C"; } }; class D {};
定义了一个std::variant<B*, C*, D*>类型的变量bcd,它可能存储B*或C*类型的值(也可能是D*)。程序需要将其内部值提取为超类A*类型,传入接收A*作为参数的方法。
显式指定类型(如A* a = std::get<C*>(bc);)可以正常运行,但无法提前知晓variant当前存储的类型。
尝试过两种方案均失败:
- 将
A*加入variant,使用std::get<A*>(bc)访问,触发bad variant access; - 尝试访客模式实现转换,代码如下:
class Visitor { template<typename TType> TType operator()(TType arg) { return arg; } }; A* a2 = std::visit(visitor, bc);
编译报错:no type named ‘type’ in ‘std::conditional_t‘。
请问是否存在无需逐个使用std::get_if判断类型的优雅实现方式?
解决方案
可以通过定制化的访客类实现类型统一转换,核心是让访客针对所有variant成员类型返回一致的A*类型(或处理非A子类的情况),确保std::visit能推导返回类型。
基础实现版本
struct ToAVisitor { // 对B*、C*直接向上转型为A* A* operator()(B* ptr) const { return ptr; } A* operator()(C* ptr) const { return ptr; } // 对D*返回nullptr(也可根据需求抛出异常) A* operator()(D* ptr) const { return nullptr; } }; // 使用方式 A* a = std::visit(ToAVisitor{}, bcd);
原访客模式失败原因
你之前的访客类返回模板参数TType,导致访问不同类型时返回B*、C*、D*三种不同类型,std::visit无法确定统一的返回类型,因此编译报错。定制化访客强制所有分支返回A*,类型统一后即可正常编译。
通用模板版本(扩展性更强)
如果后续有更多A的子类加入variant,可借助模板简化实现,避免逐个重载:
#include <type_traits> struct ToAVisitor { // 匹配所有A的子类指针,自动转型为A* template<typename T> std::enable_if_t<std::is_base_of_v<A, std::remove_pointer_t<T>>, A*> operator()(T ptr) const { return ptr; } // 匹配非A子类的指针,返回nullptr template<typename T> std::enable_if_t<!std::is_base_of_v<A, std::remove_pointer_t<T>>, A*> operator()(T ptr) const { return nullptr; } };
这个版本会自动识别所有继承自A的指针类型并完成转型,其他类型返回nullptr,无需为每个新子类添加重载。
内容的提问来源于stack exchange,提问作者wittn
相关产品推荐
相关产品推荐

