为何std::visit不接受派生类对象作为合法可调用参数?
关于std::visit无法接受派生类访问者对象的问题
问题场景
- 声明了变体
Amalgamation,以及作为该变体访问者基类的可调用类BaseVisitor; - 定义了继承自
BaseVisitor的派生类CheckBooleanVisitor,仅重写了处理bool类型的operator(); - 直接传入
CheckBooleanVisitor对象给std::visit时,VS2022报错“no instance of function template "std::visit" matches the argument list”; - 但将其转换为
BaseVisitor引用传入,或直接传入BaseVisitor实例时,程序可正常运行。
原因解析
std::visit是模板函数,它会在编译期推导传入的访问者类型,并严格检查该类型是否对std::variant的所有备选类型都提供了可调用的operator()重载。
当你直接传入CheckBooleanVisitor对象时,模板推导的类型是CheckBooleanVisitor本身。此时编译器会把这个派生类当作独立类型检查:你只重写了bool类型的operator(),其他变体类型的处理重载仅存在于基类BaseVisitor中——虽然派生类能继承基类的成员函数,但在模板推导的语境下,编译器不会自动将基类的重载归为派生类的能力,因此判定它不满足std::visit的要求,触发编译错误。
而当你将派生类对象转换为BaseVisitor引用(或直接传入基类实例)时,模板推导的类型变为BaseVisitor&(或BaseVisitor)。此时编译器检查的是基类的能力:既然BaseVisitor是作为变体的合法访问者设计的,它必然拥有针对Amalgamation所有成员类型的operator()重载(可能是默认实现、空实现或纯虚函数),因此符合std::visit的调用条件,编译通过。
可选解决方案
如果想直接传入派生类对象让std::visit接受,有两种简单方案:
- 在派生类中添加
using BaseVisitor::operator();,将基类的所有operator()重载引入派生类作用域,让编译器识别到派生类拥有所有必要的处理能力; - 手动指定
std::visit的模板参数为BaseVisitor&,强制推导基类类型,比如:std::visit<BaseVisitor&>(checkVisitor, amalgamation);
内容的提问来源于stack exchange,提问作者Vu Tung Lam
相关产品推荐
相关产品推荐

