尝试使用std :: visit和lambda表达式从std :: variant返回值 [英] Trying to return the value from std::variant using std::visit and a lambda expression

查看:75
本文介绍了尝试使用std :: visit和lambda表达式从std :: variant返回值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设存在定义如下的变体v:

Suppose there exists a variant v defined as follows:

std::variant<int,char,double,bool,std::string> v;

我正在尝试使用std :: visit或std从std :: variant获取基础值:: get。

I am trying to get the underlying value from a std::variant using std::visit or std::get.

我尝试过天真的做法:

constexpr size_t idx = v.index();
auto k = std::get<idx>(v);

但是随后得知,如果变量v不是constexpr本身,这将失败。即使这样,使用std :: string还是有问题的(由于std :: string的析构函数的定义)。

But then learned that this will fail if the variant v is not a constexpr itself. And even then there may be problems with using std::string (due to the definition of the destructor for std::string).

我的第二次尝试是尝试请执行以下操作:

My 2nd attempt was to try and do the following:

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表达式,该表达式采用了所有可能的变体类型并返回了基础值,但是似乎我误会了一些东西。

I'm stuck as to why the std::visit call does not work. I thought I supplied a trivial lambda expression which takes all possible types for the variant and returns the underlying value but it appears I am misunderstanding something.

我想使用std: :现在变体(最初考虑了std :: any后(请参见避免使用std :: any 编写相同的重复类型检查代码,但我需要一种返回所包含值的方法,我们将不胜感激。非常感谢。

I want to use std::variant now (after initially considering std::any (see avoid writing the same repetitive type-checking code with std::any) but I need a way to return the contained value. Any help would be greatly appreciated. Thank you very much.

推荐答案


我正在尝试使用
std :: visit或std从std :: variant获取基础值:: get。

I am trying to get the underlying value from a std::variant using std::visit or std::get.

如果您确实要保留基础当前值,则必须让访问支持特定的处理例如,每一种可能都是这样的:

If what you want is indeed holding the underlying current value, then you must have the visitation support a specific handling of each one possible, for instance, like this:

#include <string>
#include <variant>

int main()
{
    using your_variant = std::variant<int,char,double,bool,std::string>;
    your_variant v;

    std::visit([](your_variant&& arg) {
        if (std::holds_alternative<int>(arg))
            auto v_int = std::get<int>(arg);
        else if (std::holds_alternative<char>(arg))
            auto v_chart = std::get<char>(arg);
        else if (std::holds_alternative<double>(arg))
            auto v_double = std::get<double>(arg);
        else if (std::holds_alternative<bool>(arg))
            auto v_bool = std::get<bool>(arg);
        else if (std::holds_alternative<std::string>(arg))
            auto v_str = std::get<std::string>(arg);
        }, v);

    return 0;
}

这是因为 C ++是静态类型语言,例如,必须在编译时知道所有类型的变量。因此,编译器不能只允许声明 auto 并在需要什么可能是的各种类型之一时进行处理。在运行期间,std :: variant 可能会保持为作为当前值。

This is becasue C++ is a static typed language, as in, all types of variables must be known in compile time. Thus, the compiler cannot allow you to just declare auto and be done with it when you want what could be one of various types that the std::variant may hold as the current value at that moment during run-time.


...但是我需要一种方法来返回包含的值。

... but I need a way to return the contained value.

是静态类型的,在C ++中,如果不经历可能的情况,就无法这样做。如果您想要一个采用这样的 std :: variant 的实例并返回例如 std :: string ,然后您可以修改上面的代码,为从 std :: get() std :: to_string() c $ c>(冗余但仅用于说明)。然后,您将在非 std :: variant 形式中具有包含类型

Being statically typed, there's no way in C++ to do so without going through the possible cases. If you want a function that takes an instance of such std::variant and returns, say, a std::string, then you can modify the above code to return std::to_string() for each value returned from std::get() (redundant but just for illustration). Then you'll have the contained type in the "non-std::variant form".

从评论中选取:


然后为什么std :: visit([](auto&& arg){ std :: cout<< arg;},v);

Then why does std::visit([](auto&& arg){std::cout << arg;}, v); work?

之所以起作用,是因为您没有尝试将基础类型的变量分配/复制到您自己的变量中。同样,这将需要在编译期间知道此类变量的类型。但是,当需要 std :: variant 提供其当前持有价值的字符串表示时,例如,由于 $ std :: cout 的运算符<< -然后内部执行的操作与 if - else 上面的开关,即对该变量的每种可能的基础类型进行不同的处理>实例。

This works because you're not trying to assign/copy the variable of the underlying type into a variable of your own. This, again, would have required knowing the type for such a variable during compilation. But when std::variant is being required to provide a string representation of it's currently held value -- for example due to operator << of std::cout -- then internally what it does is of the same semantics as our if-else switch above, i.e. handling differently for each possible underlying type of this variant instance.

说明:显然,有多种方法可以指定处理的各种可能性。 std :: variant 实例当前可能正在保存。例如,如 std :: visit cppreference页面,则可能使用的是 模板推导指南 基于 std :: visit(重载{... 的实现方式,尽管可以说更好,更短的代码,需要更深入的解释才能理解我的理解方式,因为它包括从lambda继承等等,因此我认为这超出了此答案的解释范围关于我如何理解所问问题的信息。您可以阅读所有有关它的信息此处此处。或者更容易看到该问题示例的另一个用法中的用法代码示例。

Clarification: There is obviously more than one way to specify handling of the different possibilities of what the std::variant instance might currently be holding. For example, as shown in the std::visit cppreference page, you could be using the template deduction guides based std::visit(overloaded { ... way of doing it, that while arguably makes for better and shorter code, it takes some deeper explaining to understand the mechanics of, the way I see it, as it includes inheriting from a lambda, among other things, and so I figured it to be beyond the explanatory scope of this answer in regards to how I understand the question being asked. You can read all about it here and here. Or easier see the usage code example in another answer to this question.

关于编译错误:这将为您编译很好,但是并不能实现您想要的结果:

Regarding the compilation errors: This will compile for you just fine, but it doesn't achieve what you wanted:

using your_variant = std::variant<int,char,double,bool,std::string>;
your_variant v;

auto k = std::visit([](auto arg)-> your_variant {return arg;}, v);

由于lambda需要通过-> your_variant 是显式的,因为编译器无法从lambda进行推断。

Your lines didn't compile as the lambda needs to declare it's return type by -> your_variant explicitly as the compiler has no way of inferring it from the lambda.

另一种有效的语法来解决相同的问题,就是声明参数类型,这样编译器就可以知道返回的内容,就好像它是返回 auto 的函数一样:

Another valid syntax to solve the same problem is just declaring the parameter type, so the compiler can know what it's returning as if it was a function returning auto:

auto k2 = std::visit([](your_variant arg) {return arg;}, v);

这样做的编译问题:

constexpr size_t idx = v.index();
auto k = std::get<idx>(v);

再次是由于静态键入,因此 v 可以在运行时保存其任何一个索引,并且 template参数用于 std :: get()需要在编译时知道。

is again, due to static typing, that v could hold any single one of its indices at run-time, and the template argument for std::get() needs to be known at compile time.

这篇关于尝试使用std :: visit和lambda表达式从std :: variant返回值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆