Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何多态地使用std::variant的替代类型_C++_Std Variant - Fatal编程技术网

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 implu
std::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&)