C++ 如何多态地使用std::variant的替代类型
我正在编写一个解析器,并使用C++ 如何多态地使用std::variant的替代类型,c++,std-variant,C++,Std Variant,我正在编写一个解析器,并使用std::variant来表示AST的节点。对于基本表达式,我有如下内容: struct Add; struct Sub; struct Mul; struct Div; typedef std::variant<Add, Sub, Mul, Div> Node; struct-Add; 结构子系统; 结构Mul; 结构分区; typedef std::variant节点; (实际的结构定义在后面,因为其中一些是指节点) 我发现这种方法比将Node声明
std::variant
来表示AST的节点。对于基本表达式,我有如下内容:
struct Add;
struct Sub;
struct Mul;
struct Div;
typedef std::variant<Add, Sub, Mul, Div> Node;
struct-Add;
结构子系统;
结构Mul;
结构分区;
typedef std::variant节点;
(实际的结构定义在后面,因为其中一些是指节点
)
我发现这种方法比将Node
声明为抽象基类和Add
等声明为子类要好,因为它使我能够将使用AST的逻辑与AST本身分开。例如,计算表达式的代码可以使用std::visit
,而不需要使用虚拟的evaluate
方法或执行类型检查和向下转换
我的问题是,我希望每个节点都有一些字段,我希望在使用这些字段时能够对所有节点变量一视同仁
我唯一的策略是:
- 将
节点
定义为具有公共字段和单独的std::variant
成员的结构
- 在每个
节点
备选方案中分别定义字段,并为每个字段定义一个具有选择该字段的const auto&
成员的访问者。换句话说,只需要使用访问者
还有别的办法吗?我真正想做的是用字段定义一个抽象基类,让所有的节点
替代项(添加
等)从该类继承,然后能够说,“我不知道这个std::variant
包含什么替代方案,但我知道它们都是这个基类的实例,我只想将它用作该类的实例。”假设基类偏移量会因类而异(通常不能保证基类在C++中的偏移量为零)。显然,这意味着您需要知道变量中存储的实际类型,才能定位基类子对象
一旦你意识到这一点,你就会明白为什么你的选择是有效的:
- 如果公共字段是
节点
的一部分,则它们在节点
- 如果使用访问者,它将查找实际类型,因此可以计算基类子对象的每类型偏移量
假设变量中的每个类型都有一些字段std::string\u视图标记代码>(这是在每个类中写出来的,还是来自一个普通的、不一定是多态的基类,取决于您),您可以编写如下访问器:
std::string_view getToken(const Node& node)
{
return std::visit([](const auto& n) { return n.token; }, node);
}
请注意,您不必手动添加重载-lambda中的auto
实际上使lambda成为模板,因此您只需依赖编译时多态性。您将得到一个漂亮的跳转表(每个跳转表只返回适当的成员偏移):
std::\uu详细信息::\uu变体::\uu gen\u vtable\u implustd::visit的functor不需要为每个可选项都指定特定的重载,如果一个重载覆盖了其中的几个(与基类一样),那么就可以了。是什么阻止了所有类继承自一个公共基类,然后将它们全部包含在一个变体中?如果变量的所有成员都继承自同一个类,那么您检查的对象并不重要,您可以可靠地访问这些基类成员。@Jarod42如果我使用std::visit
,那么所有替代项是否继承自基类似乎并不重要。我修改了Max的示例,以便所有备选方案都继承自Base
,并为const Base&
定义了一个访问者,它使用const auto&
生成了与他的示例完全相同的调度逻辑。这与MSalters的观点一致,即即使所有备选方案都继承自基类,它们也会得到相同的处理。@NathanOliver说,如果不确切知道它包含哪些备选方案,或者不使用访问者来处理所有情况,就无法使用std::variant
。即使我知道所有备选方案都继承自Base
,我也不能只获取备选方案0,然后将其转换为Base
引用。这将适用于联合,但是变量被标记,代码检查以确保变量实际包含您尝试使用的替代方案。我的意思是:“……即使所有替代方案都继承自基类,它们也不能被完全相同地对待。”感谢所有的详细信息。我对visitor方法感觉更好,因为它将使用跳转表,这不会比调用虚拟方法更昂贵。对。我在使用java之后回到了C++,所以我仍然处在单一的基类框架中。
std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 0ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&): # @"std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 0ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)"
mov rax, qword ptr [rsi + 8]
mov rdx, qword ptr [rsi + 16]
ret
std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 1ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&): # @"std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 1ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)"
mov rax, qword ptr [rsi + 16]
mov rdx, qword ptr [rsi + 24]
ret
std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 2ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&): # @"std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 2ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)"
mov rax, qword ptr [rsi + 8]
mov rdx, qword ptr [rsi + 16]
ret
std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 3ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&): # @"std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 3ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)"
mov rax, qword ptr [rsi + 16]
mov rdx, qword ptr [rsi + 24]
ret
std::__detail::__variant::__gen_vtable<true, std::basic_string_view<char, std::char_traits<char> >, getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&>::_S_vtable:
.quad std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 0ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)
.quad std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 1ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)
.quad std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 2ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)
.quad std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<std::basic_string_view<char, std::char_traits<char> > (*)(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)>, std::tuple<std::variant<Add, Sub, Mul, Div> const&>, std::integer_sequence<unsigned long, 3ul> >::__visit_invoke(getToken(std::variant<Add, Sub, Mul, Div> const&)::$_0&&, std::variant<Add, Sub, Mul, Div> const&)