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

cannot compile below code for visiting a variant of struct

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

#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 要求 return 类型是所有组合的相同类型和值类别:

The return type is deduced from the returned expression as if by decltype. The call is ill-formed if the invocation above is not a valid expression of the same type and value category, for all combinations of alternative types of all variants.

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

Requires: For each valid pack m, e(m) shall be a valid expression. All such expressions shall be of the same type and value category; otherwise, the program is ill-formed.

例如,如果每个结构都有一个 int 成员 x 而你 return 它将不再是格式错误的 see it live on godbolt. In your case you could change the visitor to simply print instead of return the value see it live:

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