C++17之std::visit的具体使用

 更新时间:2022年02月07日 09:47:47   作者:云洞  
本文主要介绍了C++17之std::visit的具体使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    它们必须明确地为每种可能的类型提供函数调用操作符。然后,使用相应的重载来处理当前的备选项类型。

1. 使用对象函数方式访问

例1:

#include <iostream>
#include <variant>
#include <string>
 
struct MyVisitor
{
    void operator()(double d) const {
        std::cout << d << '\n';
    }
    void operator()(int i) const {
        std::cout << i << '\n';
    }
    void operator()(const std::string& s) const {
        std::cout << s << '\n';
}
};
int main()
{
    std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit");
 
    std::visit(MyVisitor(), var1); // calls operator() for matching int type
 
    std::visit(MyVisitor(), var2); // calls operator() for matching double type
 
    std::visit(MyVisitor(), var3); // calls operator() for matching std::string type
 
    return 0;
}

结果如下:

 如果操作符()不支持所有可能的类型,或者调用不明确,则visit()调用是编译时错误。还可以使用访问者修改当前类型的值(但不能分配新类型的值)。

例2:

#include <iostream>
#include <variant>
#include <string>
 
struct Twice
{
    void operator()(double& d) const {
        d *= 2;
    }
    void operator()(int& i) const {
        i *= 2;
    }
    void operator()(std::string& s) const {
        s = s + s;
    }
};
 
int main()
{
    std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit");
 
    std::visit(Twice(), var1); // calls operator() for matching int type
 
    std::visit(Twice(), var2); // calls operator() for matching double type
 
    std::visit(Twice(), var3); // calls operator() for matching std::string type
 
    std::cout << std::get<int>(var1) << std::endl;
    std::cout << std::get<double>(var2) << std::endl;
    std::cout << std::get<std::string>(var3) << std::endl;
 
    return 0;
}

结果如下:

注意,对象操作符应该为const函数,因为它们是无状态的(它们不改变它们的行为,只改变传递的值,即不改变成员变量的值)。 

 2. 使用泛型Lambdas访问

使用这个特性最简单的方法是使用泛型lambda,它是一个函数对象,用于任意类型:

例3:

#include <iostream>
#include <variant>
#include <string>
 
auto printvariant = [](const auto& val) 
{
    std::cout << val << std::endl;
};
 
int main()
{
    std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit");
 
    std::visit(printvariant, var1);
 
    std::visit(printvariant, var2);
 
    std::visit(printvariant, var3);
 
    return 0;
}

结果如下:

 这里,泛型lambda定义了一个闭包类型,其中函数调用操作符作为成员模板:

class CompilerSpecifyClosureTypeName 
{
public:
template<typename T>
auto operator() (const T& val) const 
{
    std::cout << val << '\n';
}
};

也可以使用lambda来修改当前选项的值:

例4:

#include <iostream>
#include <variant>
#include <string>
 
auto printvariant = [](const auto& val)
{
    std::cout << val << std::endl;
};
 
int main()
{
    std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit");
 
    std::visit([](auto& val) {
        val = val + val;
        },
        var1);
    std::visit([](auto& val) {
        val = val + val;
        },
        var2);
    std::visit([](auto& val) {
        val = val + val;
        },
        var3);
 
    std::visit(printvariant, var1);
    std::visit(printvariant, var2);
    std::visit(printvariant, var3);
 
    return 0;
}

结果如下:

甚至可以使用编译时if语言特性以不同的方式处理不同的备选值:

例5:

#include <iostream>
#include <variant>
#include <string>
 
auto dblvar = [](auto& val)
{
    if constexpr (std::is_convertible_v<decltype(val), std::string>)
    {
        val = val + " test";
    }
    else
    {
        val += 2;
    }
};
 
int main()
{
    std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit");
 
    std::visit(dblvar, var1);
    std::visit(dblvar, var2);
    std::visit(dblvar, var3);
 
    std::cout << std::get<int>(var1) << std::endl;
    std::cout << std::get<double>(var2) << std::endl;
    std::cout << std::get<std::string>(var3) << std::endl;
 
    return 0;
}

这里,对于一个std::string类型备选项,泛型lambda的调用实例化它的泛型函数调用模板来计算:

val = val + “ test”;

而对于其他类型备选项,如int或double, lambda的调用实例化其通用函数调用模板来计算:

val += 2;

结果如下:

3. 使用重载的Lambdas来访问

通过为函数对象和lambdas使用一个重载器,还可以定义一组lambdas,其中使用最佳匹配作为访问者。假设,重载器定义为重载,如下所示:

template<typename... Ts>
struct overload : Ts...
{
using Ts::operator()...;
};
// base types are deduced from passed arguments:
template<typename... Ts>
overload(Ts...) -> overload<Ts...>;

可以使用重载访问一个变量,为每个选项提供lambdas:

std::variant<int, std::string> var(42);
...
std::visit(overload{ // calls best matching lambda for current alternative
[](int i) { std::cout << "int: " << i << '\n'; },
[](const std::string& s) {
std::cout << "string: " << s << '\n'; },
},
var);

还可以使用泛型lambda。总是用最好的搭配。例如,要修改variant对象的当前类型备选项的值,可以使用重载将字符串和其他类型的值“加倍”:

auto twice = overload{
[](std::string& s) { s += s; },
[](auto& i) { i *= 2; },
};

    使用此重载,对于字符串类型备选项,将添加当前值,而对于所有其他类型,将值乘以2,这演示了variant对象的以下应用程序:

std::variant<int, std::string> var(42);
std::visit(twice, var); // value 42 becomes 84
...
var = "hi";
std::visit(twice, var); // value "hi" becomes "hihi"

例 6:

#include <iostream>
#include <variant>
#include <string>
 
template<typename... Ts>
struct overload : Ts...
{
    using Ts::operator()...;
};
 
template<typename... Ts>
overload(Ts...)->overload<Ts...>;
 
auto twice = overload{
        [](std::string& s) { s += s; },
        [](auto& i) { i *= 2; },
};
 
int main()
{
    std::variant<int, std::string> var1(42) , var3("visit");
 
    std::visit(twice, var1);
    std::visit(twice, var3);
    
    std::visit(overload{ // calls best matching lambda for current alternative
        [](int i) { std::cout << "int: " << i << '\n'; },
        [](const std::string& s) {
        std::cout << "string: " << s << '\n'; },
        },
        var1);
    
    std::visit(overload{ // calls best matching lambda for current alternative
       [](int i) { std::cout << "int: " << i << '\n'; },
       [](const std::string& s) {
       std::cout << "string: " << s << '\n'; },
        },
        var3);
    
    return 0;
}

结果如下:

 到此这篇关于C++17之std::visit的具体使用的文章就介绍到这了,更多相关C++17 std::visit内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言实现大学生考勤管理系统

    C语言实现大学生考勤管理系统

    这篇文章主要为大家详细介绍了C语言实现大学生考勤管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • C语言链表实现贪吃蛇游戏

    C语言链表实现贪吃蛇游戏

    这篇文章主要为大家详细介绍了C语言链表实现贪吃蛇游戏源码,适合C语言入门者学习阅读,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • 了解C++编程中指定的异常和未经处理的异常

    了解C++编程中指定的异常和未经处理的异常

    这篇文章主要介绍了C++中指定的异常和未经处理的异常,介绍了有关noexcept和terminate的作用,并结合了C++11标准的新特性,需要的朋友可以参考下
    2016-01-01
  • 一文搞懂C语言中的文件操作

    一文搞懂C语言中的文件操作

    文件操作想必大家掌握的并不熟练,确实因为我们用的并不多,而本节内容能够让大家初步认识文件操作,从文件认识到文件使用,让我们对c语言文件操作有个初步的了解
    2022-11-11
  • 实例讲解C++设计模式编程中State状态模式的运用场景

    实例讲解C++设计模式编程中State状态模式的运用场景

    这篇文章主要介绍了实例讲解C++设计模式编程中State状态模式的运用场景,文章最后的适用性部分则介绍了一些State模式善于处理的情况,需要的朋友可以参考下
    2016-03-03
  • 使用C++制作简单的web服务器

    使用C++制作简单的web服务器

    本文给大家分享的是使用C++简单实现web服务器的代码,虽然非常的简陋,功能也很少,主要是为了更好的理解WEB服务器的工作原理,推荐给大家,也希望对大家能够有所帮助。
    2015-03-03
  • Embarcadero Dev-C++输出中文乱码问题图文详解

    Embarcadero Dev-C++输出中文乱码问题图文详解

    Dev-C++(或者叫做 Dev-Cpp)是Windows环境下的一个轻量级C/C++ 集成开发环境(IDE),下面这篇文章主要给大家介绍了关于Embarcadero Dev-C++输出中文乱码问题的相关资料,需要的朋友可以参考下
    2023-01-01
  • C语言UDP传输系统源码

    C语言UDP传输系统源码

    这篇文章主要为大家分享了C语言UDP传输系统的源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • 详解C++中对构造函数和赋值运算符的复制和移动操作

    详解C++中对构造函数和赋值运算符的复制和移动操作

    这篇文章主要介绍了C++中对构造函数和赋值运算符的复制和移动,是C++入门学习中的基础知识,需要的朋友可以参考下
    2016-01-01
  • C语言异或校验算法的项目实现

    C语言异或校验算法的项目实现

    异或校验算法(XOR校验)是一种简单的校验算法,用于检测数据在传输或存储过程中是否发生了错误,本文主要介绍了C语言异或校验算法的项目实现,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08

最新评论