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

目录
  • 1. 使用对象函数方式访问
  • 2. 使用泛型Lambdas访问
  • 3. 使用重载的Lambdas来访问

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

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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++17中的std::optional的具体使用

    直入主题 本篇之中,仅仅述及 std::optional ,其它和 variant 相关的话题以后再说吧. std::optional 也划入 variant 类别中,其实它还是谈不上可称为变体类型的,但新版本中的三大件(optional,any and variant)也可以归一类无妨. C++17 之前 在 C 时代以及早期 C++ 时代,语法层面支持的 nullable 类型可以采用指针方式: T* ,如果指针为 NULL (C++11 之后则使用 nullptr ) 就表示无值状态(em

  • C++17 使用 std::string_view避免字符串拷贝优化程序性能

    C++中std::string是日常Coding中经常使用的一个类,使用起来非常方便,但是也存在一些弊端. 如下代码,参数传递的过程发生了内存分配(Memory Allocation)和内存拷贝. void fun(const std::string& s) { std::cout << s << std::endl; } const char* ch = "hello world"; // bad way, expensive if the strin

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

    目录 1. 使用对象函数方式访问 2. 使用泛型Lambdas访问 3. 使用重载的Lambdas来访问 它们必须明确地为每种可能的类型提供函数调用操作符.然后,使用相应的重载来处理当前的备选项类型. 1. 使用对象函数方式访问 例1: #include <iostream> #include <variant> #include <string> struct MyVisitor { void operator()(double d) const { std::cou

  • C++17之std::any的具体使用

    目录 1. 使用std::any 2. std::any类型和操作 2.1 std::any的类型 2.2 std::any操作 一般来说,c++是一种具有类型绑定和类型安全性的语言.值对象声明为具有特定类型,该类型定义哪些操作是可能的以及它们的行为方式.值对象不能改变它们的类型. std: any是一种值类型,它能够更改其类型,同时仍然具有类型安全性.也就是说,对象可以保存任意类型的值,但是它们知道当前保存的值是哪种类型.在声明此类型的对象时,不需要指定可能的类型. 诀窍在于,对象同时拥有包含

  • C++17使用std::optional表示可能存在的值

    目录 前言 返回一个bool值 使用 std::optional 改写 总结 前言 平时写代码会遇到一种传递参数特殊值标记特殊流程,或者函数返回值存在魔法数的情况,很需要一种标记参数或返回值状态的结构,那么在 C++17 标准下提供了 std::optional 这个模板类,可以表示一个值不存在的状态,一起来看看用法吧. 返回一个bool值 以下例子纯属虚构,只为说明问题,无实际意义 bool getBoolVal(int a, int b) {     int* n = new int;  

  • C++ 11实现检查是否存在特定的成员函数

    问题提出 最近工作中遇到这样一个需求:实现一个ToString函数将类型T转换到字符串,如果类型T中含有同名方法ToString则直接调用. 这样一个ToString实现可以使用std::enable_if来做到,但是这里的难点在于如何判断类型T中存在这样一个ToString方法,以便可以放入enable_if中做SFINAE. 检查类中是否存在特定成员 相同的问题在知乎上有人提出过,@孙明琦的答案提供了一个用于检测特定检测子U在类型T下是否有效的检测器is_detected_v.其中用到了一个

  • C++实现俄罗斯方块游戏

    本文实例为大家分享了C++实现俄罗斯方块游戏的具体代码,供大家参考,具体内容如下 使用VS2017编译 思路: 1.打印出游戏地图,也就是设计游戏地图类game_interdace,包括设计游戏开始界面的函数,游戏地图界面的函数,游戏结束界面的函数,以及设计一些辅助函数如(设置光标为目标点,改变颜色,隐藏光标)来完成上述设计. 2.设计方块图形类,包括生成图形,清理图形,图形移动,图形停止的处理,图形的消失(实质是得分)等. #include <iostream> #include<co

  • C++ 多线程之互斥量(mutex)详解

    目录 std::mutex std::recursive_mutex std::time_mutex std::recursive_timed_mutex std::shared_mutex std::shared_timed_mutex 总结 C++ 11中的互斥量,声明在 <mutex> 头文件中,互斥量的使用可以在各种方面,比较常用在对共享数据的读写上,如果有多个线程同时读写一个数据,那么想要保证多线程安全,就必须对共享变量的读写进行保护(上锁),从而保证线程安全. 互斥量主要有四中类型

  • C++精要分析lambda表达式的使用

    目录 引言 语法与示例 捕获列表 捕获引用 特殊用法 实现原理 应用 引言 C++要走向现代语言,如果不支持lambda表达式,你很难认为这门语言和现代有什么关系.幸好,从C++11标准起,它就实现了对lambda表达式的支持. 那么,什么是lambda表达式呢? lambda表达式是匿名函数,就是说不用定义函数名,函数实现可以直接嵌入在业务逻辑代码中.诸如python.java.C#等语言,都将其作为基础特性. 其优点是提高了代码的可读性,对于一些无需重用的方法特别适合.例如在容器的迭代中实现

  • 浅谈 C++17 里的 Visitor 模式

    目录 一.Visitor Pattern 1.组成 2.接口 3.场景 4.特点 5.实现 二.Epilogue 一.Visitor Pattern 访问者模式是一种行为模式,允许任意的分离的访问者能够在管理者控制下访问所管理的元素.访问者不能改变对象的定义(但这并不是强制性的,你可以约定为允许改变).对管理者而言,它不关心究竟有多少访问者,它只关心一个确定的元素访问顺序(例如对于二叉树来说,你可以提供中序.前序等多种访问顺序). 1.组成 Visitor 模式包含两个主要的对象:Visitab

随机推荐