假设存在一个变体 v,定义如下:
std::variant<int,char,double,bool,std::string> v;
我正在尝试使用 std::visit 或 std::get 从 std::variant 获取基础值。
我尝试天真地这样做:
constexpr size_t idx = v.index();
auto k = std::get<idx>(v);
但是后来了解到,如果变体 v 本身不是 constexpr ,这将失败。即便如此,使用 std::string 也可能存在问题(由于 std::string 的析构函数的定义)。
我的第二次尝试是尝试执行以下操作:
auto k = std::visit([](auto arg){return arg;}, v);
但是收到了这个:
$g++ -o main *.cpp --std=c++17
In file included from main.cpp:5:0:
/usr/include/c++/7/variant: In instantiation of ‘static constexpr auto std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...)>, std::tuple<_Tail ...>, std::integer_sequence<long unsigned int, __indices ...> >::_S_apply() [with _Result_type = int; _Visitor = main()::<lambda(auto:1)>&&; _Variants = {std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&}; long unsigned int ...__indices = {1}]’:
/usr/include/c++/7/variant:663: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<int (*)(main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)>; _Result_type = int; _Visitor = main()::<lambda(auto:1)>&&; long unsigned int ...__dimensions = {5}; _Variants = {std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&}; long unsigned int ...__indices = {}]’
/usr/include/c++/7/variant:651:39: required from ‘constexpr const std::__detail::__variant::_Multi_array<int (*)(main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&), 5> std::__detail::__variant::__gen_vtable<int, main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&>::_S_vtable’
/usr/include/c++/7/variant:704:29: required from ‘struct std::__detail::__variant::__gen_vtable<int, main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&>’
/usr/include/c++/7/variant:1239:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main()::<lambda(auto:1)>; _Variants = {std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&}]’
main.cpp:89:49: required from here
/usr/include/c++/7/variant:704:49: in constexpr expansion of ‘std::__detail::__variant::__gen_vtable<int, main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&>::_S_apply()’
/usr/include/c++/7/variant:701:38: in constexpr expansion of ‘std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<int (*)(main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&), 5>, std::tuple<std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&>, std::integer_sequence<long unsigned int> >::_S_apply()’
/usr/include/c++/7/variant:641:19: in constexpr expansion of ‘std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<int (*)(main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&), 5>, std::tuple<std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&>, std::integer_sequence<long unsigned int> >::_S_apply_all_alts<0, 1, 2, 3, 4>(\xe2\x80\x98result_dec\xe2\x80\x99 not supported by dump_expr#<expression error>, (std::make_index_sequence<5>(), std::make_index_sequence<5>()))’
/usr/include/c++/7/variant:686:43: error: invalid conversion from ‘std::__success_type<char>::type (*)(main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&) {aka char (*)(main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)}’ to ‘int (*)(main()::<lambda(auto:1)>&&, std::variant<int, char, double, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’ [-fpermissive]
{ return _Array_type{&__visit_invoke}; }
^
我不知道为什么 std::visit 调用不起作用。我以为我提供了一个简单的 lambda 表达式,它采用变量的所有可能类型并返回基础值,但似乎我误解了一些东西。
我现在想使用 std::variant (在最初考虑 std::any 之后(请参阅 避免使用 std::any 编写相同的重复类型检查代码),但我需要一种方法来返回包含的值。任何帮助都会很大非常感谢。
原文由 markf78 发布,翻译遵循 CC BY-SA 4.0 许可协议
如果您想要的确实是持有基础当前值,那么您必须让访问支持对每个可能的特定处理。例如,像这样:
这是因为 C++ 是一种 静态类型 语言,如在 编译时 必须知道所有类型的变量。因此,编译器不允许您仅声明
auto
并在您想要std::variant
可能作为当前值的各种类型之一 时使用它在 运行时。由于是静态类型的,C++ 中没有办法在不经历可能的情况下这样做。如果您想要一个调用
std::variant
的实例并返回例如std::string
,那么您可以修改上面的代码以返回std::to_string(var)
为每个上述情况if
/else
语句。constexpr if
关键字。值得一读,以防不清楚为什么这里需要这样做。需要使用std::decay_t
模板来确保在std::is_same_v
模板中比较的类型是基本( 非常量和非引用 限定)类型。摘自评论:
这是有效的,因为您没有尝试将基础类型的变量分配/复制到您自己的变量中。同样,这需要在编译期间知道此类变量的类型。但是当
std::variant
被要求提供其当前持有值的 字符串表示- 例如由于operator <<
的std::cout
那么它在内部做什么与我们的if
-else
上面的开关具有相同的语义,即对这个variant
实例的每种可能的底层类型进行不同的处理。澄清: 显然有不止一种方法可以指定处理
std::variant
实例当前可能持有的不同可能性。例如,如std::visit
cppreference page 所示,您可以使用基于std::visit(overloaded { ...
的 模板推导指南 这样做,虽然可以说可以使代码更好更短,但它需要一些更深入的解释来理解我看到它的机制,因为它包括从 lambda 继承等等,所以我认为它超出了这个答案的解释范围,关于我如何理解这个问题是问。您可以 在此处 和 此处 阅读所有相关信息。或者更容易在此问题的另一个答案中查看使用代码示例。关于编译错误:这将为您编译就好,但它没有达到您想要的效果:
您的行没有编译,因为 lambda 需要通过
-> your_variant
显式声明它的返回类型,因为编译器无法从 lambda 推断它。解决相同问题的另一种有效语法只是声明参数类型,因此编译器可以知道它返回什么,就好像它是一个返回
auto
的函数一样:这样做的编译问题:
再次,由于 _静态类型_,
v
可以在运行时保存其任何一个索引,并且std::get()
的 模板参数 需要在编译时知道。