无法编译以下代码以访问结构的变体

Posted

技术标签:

【中文标题】无法编译以下代码以访问结构的变体【英文标题】:cannot compile below code for visiting a variant of struct 【发布时间】:2018-11-15 03:47:26 【问题描述】:

我尝试编写一个非常简单的变体和访问者实现,如下所示,但出现编译错误,我无法弄清楚原因。

#include <variant>
#include <string>
#include <iostream>

struct foo

    std::string s;
;

struct bar

    double s;
;

using var = std::variant<foo, bar>;

struct visitor

    template <class T>
    auto operator()(const T v) -> decltype(v.s)
    
        return v.s;
    
;

int main()

    foo f;
    f.s = 3.0;

    var x = f;
    auto xs = std::visit(visitor, x);
    std::cout<<xs<<std::endl;

这个错误太长了,而且复杂到让我作为 c++11+ 的新手感到困惑

In file included from main.cpp:1:
/usr/local/include/c++/8.1.0/variant: In instantiation of 'static constexpr auto std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...)>, std::tuple<_Rest ...>, std::integer_sequence<long unsigned int, __indices ...> >::_S_apply() [with _Result_type = std::__cxx11::basic_string<char> _Visitor = visitor&&; _Variants = std::variant<foo, bar>&; long unsigned int ...__indices = 1]':
/usr/local/include/c++/8.1.0/variant:825:61:   required from 'static constexpr void std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...), __dimensions ...>, std::tuple<_Variants ...>, std::integer_sequence<long unsigned int, __indices ...> >::_S_apply_single_alt(_Tp&) [with long unsigned int __index = 1; _Tp = std::__detail::__variant::_Multi_array<std::__cxx11::basic_string<char> (*)(visitor&&, std::variant<foo, bar>&)> _Result_type = std::__cxx11::basic_string<char> _Visitor = visitor&&; long unsigned int ...__dimensions = 2; _Variants = std::variant<foo, bar>&; long unsigned int ...__indices = ]'
/usr/local/include/c++/8.1.0/variant:813:39:   required from 'static constexpr void std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...), __dimensions ...>, std::tuple<_Variants ...>, std::integer_sequence<long unsigned int, __indices ...> >::_S_apply_all_alts(std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...), __dimensions ...>, std::tuple<_Variants ...>, std::integer_sequence<long unsigned int, __indices ...> >::_Array_type&, std::index_sequence<__indices ...>) [with long unsigned int ...__var_indices = 0, 1; _Result_type = std::__cxx11::basic_string<char> _Visitor = visitor&&; long unsigned int ...__dimensions = 2; _Variants = std::variant<foo, bar>&; long unsigned int ...__indices = ; std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...), __dimensions ...>, std::tuple<_Variants ...>, std::integer_sequence<long unsigned int, __indices ...> >::_Array_type = std::__detail::__variant::_Multi_array<std::__cxx11::basic_string<char> (*)(visitor&&, std::variant<foo, bar>&), 2> std::index_sequence<__indices ...> = std::integer_sequence<long unsigned int, 0, 1>]'
/usr/local/include/c++/8.1.0/variant:803:19:   required from 'static constexpr std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...), __dimensions ...>, std::tuple<_Variants ...>, std::integer_sequence<long unsigned int, __indices ...> >::_Array_type std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...), __dimensions ...>, std::tuple<_Variants ...>, std::integer_sequence<long unsigned int, __indices ...> >::_S_apply() [with _Result_type = std::__cxx11::basic_string<char> _Visitor = visitor&&; long unsigned int ...__dimensions = 2; _Variants = std::variant<foo, bar>&; long unsigned int ...__indices = ; std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...), __dimensions ...>, std::tuple<_Variants ...>, std::integer_sequence<long unsigned int, __indices ...> >::_Array_type = std::__detail::__variant::_Multi_array<std::__cxx11::basic_string<char> (*)(visitor&&, std::variant<foo, bar>&), 2>]'
/usr/local/include/c++/8.1.0/variant:863:38:   required from 'static constexpr std::__detail::__variant::__gen_vtable<_Result_type, _Visitor, _Variants>::_Array_type std::__detail::__variant::__gen_vtable<_Result_type, _Visitor, _Variants>::_S_apply() [with _Result_type = std::__cxx11::basic_string<char> _Visitor = visitor&&; _Variants = std::variant<foo, bar>&; std::__detail::__variant::__gen_vtable<_Result_type, _Visitor, _Variants>::_Array_type = std::__detail::__variant::_Multi_array<std::__cxx11::basic_string<char> (*)(visitor&&, std::variant<foo, bar>&), 2>]'
/usr/local/include/c++/8.1.0/variant:866:49:   required from 'constexpr const std::__detail::__variant::_Multi_array<std::__cxx11::basic_string<char> (*)(visitor&&, std::variant<foo, bar>&), 2> std::__detail::__variant::__gen_vtable<std::__cxx11::basic_string<char>, visitor&&, std::variant<foo, bar>&>::_S_vtable'
/usr/local/include/c++/8.1.0/variant:866:29:   required from 'struct std::__detail::__variant::__gen_vtable<std::__cxx11::basic_string<char>, visitor&&, std::variant<foo, bar>&>'
/usr/local/include/c++/8.1.0/variant:1394:23:   required from 'constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = visitor; _Variants = std::variant<foo, bar>&]'
main.cpp:34:38:   required from here
/usr/local/include/c++/8.1.0/variant:848:43: error: invalid conversion from 'std::__success_type<double>::type (*)(visitor&&, std::variant<foo, bar>&)' aka 'double (*)(visitor&&, std::variant<foo, bar>&)' to 'std::__cxx11::basic_string<char> (*)(visitor&&, std::variant<foo, bar>&)' [-fpermissive]
        return _Array_type&__visit_invoke; 

【问题讨论】:

因此您可以将您的示例更改为std::visit(printer, x); 【参考方案1】:

std::visit 要求所有组合的返回类型是相同的类型和值类别:

返回类型是从返回的表达式推导出来的,就像通过 decltype 一样。如果对于所有变体的替代类型的所有组合,上述调用不是相同类型和值类别的有效表达式,则该调用是格式错误的。

参见 C++ 标准草案[variant.visit]p2:

要求:对于每个有效的包 m,e(m) 应该是一个有效的表达式。 所有此类表达式应具有相同的类型和值类别;否则,程序是非良构的。

例如,如果每个结构都有一个 int 成员 x 并且您返回它,那么它将不再是格式错误的 see it live on godbolt。在您的情况下,您可以将访问者更改为简单地打印而不是返回值see it live:

struct visitor

    template <class T>
    void operator()(const T v) 
    
        std::cout<< v.s;
    
;

【讨论】:

以上是关于无法编译以下代码以访问结构的变体的主要内容,如果未能解决你的问题,请参考以下文章

使用访问者时提升变体转换错误

访问具有单态的变体

如何使用预设策略加密 Amazon CloudFront 签名以进行私有内容访问

非递归访问自定义变体 - 如何优雅地返回值?

无法编译访问另一个 dll 的 dll

为啥连续抛出 2 个异常不会产生无法访问的代码警告?