https://cloud.tencent.com/developer/article/1351910

[译]C++17,optional, any, 和 variant 的更多细节

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tkokof1/article/details/82660834

看到一个介绍 C++17 的系列博文(原文),有十来篇的样子,觉得挺好,看看有时间能不能都简单翻译一下,这是第六篇~

std::optional, std::any, 和 std::variant 有一个共同特点:他们都支持就地构造.另外的,std::variant 还支持访问者模式.

首先,我们要了解一下这3种数据类型的功能作用.

  • std::optional 是一种可能包含也可能不包含某一类型对象的类型.
  • std::variant 是一种类型安全的联合体
  • std::any 是一种可以包含任意类型(指可复制类型)对象的类型

我在之前的文章中讲解了这3个数据类型的一些细节,不了解的朋友可以先去看看,相关内容这里就不再赘述了.

Construct in-place

什么是就地构造呢?以 std::optional<std::string> 为例来说明就是: 所谓就地构造,就是你可以直接使用 std::string 的构造参数来构造 std::optional<std::string>.

下面是一个简短的示例.

#include <optional>
#include <iostream>
#include <string> int main()
{
std::cout << std::endl; std::optional<std::string> opt1(std::in_place, "C++17"); std::optional<std::string> opt2(std::in_place, 5, 'C'); std::optional<std::string> opt3(std::in_place, { 'C', '+', '+', '1', '7' }); std::optional<std::string> opt4(opt3); std::cout << *opt1 << std::endl;
std::cout << *opt2 << std::endl;
std::cout << *opt3 << std::endl;
std::cout << *opt4 << std::endl; std::cout << std::endl; return 0;
}

代码中的 opt1(第10行), op2(第13行) 和 op3(第16行) 都使用了 std::in_place 标记来进行构造,这意味着 std::optional 的构造参数将直接用于调用 std::string 的构造函数.所以在上述代码中, opt1 中 std::string 的构造函数参数即为 C 风格字符串(“C++17”), op2 中是5个单字符’C’, op3 中则是初始化列表({ ‘C’, ‘+’, ‘+’, ‘1’, ‘7’ }).另外,代码中的 opt4(第19行)并未使用就地构造方法,而是调用了 std::optional 的复制构造函数(复制了op3).

程序的输出如下:

上述的就地构造是不是觉得有些熟悉?其实早在 C++11 中,标准库容器就引入很多用于增加容器元素的接口方法,这些方法都以 emplace 开头,功能上就是提供了就地构造的方法.以 std::vector<int> vec 为例,借助其支持的 emplace_back 方法,我们可以直接调用 vec.emplace_back(5) 来增加 vec 的末尾元素,这等同于下面代码: vec.push_back(int(5)).

std::variant 还支持 std::visit 方法(即精典的设计模式:访问者).

Visit a list of variants

std::visit 方法允许你对一个 std::variants 列表应用访问者模式,而相应的访问者必须是一个callable类型,所谓 callable 类型,是一种可以被调用的类型,通常是一个函数,一个函数对象或者一个 lambda 函数.简单起见,这里我仅使用 lambda 函数来举例说明.

#include <iostream>
#include <vector>
#include <typeinfo>
#include <type_traits>
#include <variant> int main()
{
std::cout << std::endl; std::vector<std::variant<char, long, float, int, double, long long>>
vecVariant = { 5, '2', 5.4, 100ll, 2011l, 3.5f, 2017 }; for (auto& v : vecVariant) {
std::visit([](auto&& arg) {std::cout << arg << " "; }, v);
} std::cout << std::endl; for (auto& v : vecVariant) {
std::visit([](auto&& arg) {std::cout << typeid(arg).name() << " "; }, v);
} std::cout << std::endl; std::common_type<char, long, float, int, double, long long>::type res{}; std::cout << "typeid(res).name(): " << typeid(res).name() << std::endl; for (auto& v : vecVariant) {
std::visit([&res](auto&& arg) {res += arg; }, v);
}
std::cout << "res: " << res << std::endl; for (auto& v : vecVariant) {
std::visit([](auto&& arg) {arg *= 2; }, v);
std::visit([](auto&& arg) {std::cout << arg << " "; }, v);
} std::cout << std::endl; return 0;
}

代码中我创建了 std::variants 的列表(代码第11行).每个 variant 都可以包含以下的任一类型:char, long, float, int, double, long long.遍历 variant 列表并对每一个 variant 应用 lambda 函数非常简单(代码第15行到17行).借助 typeid 函数,我便可以获得 variant 的实际类型(代码第22行到24行).到这里,我想你应该已经看出了代码中的访问者模式, std::vector<std::variant> 就是我应用各种函数(即访问者)的被访问数据结构.

现在,我想将各个 variant 的元素求和.求和之前,我需要在编译期确定所求和的结果类型,为此我使用了 std::common_type (代码第29行), std::common_type 可以给出 char, long, float, int, double, 和 long long 都可以进行隐式转换的类型(double类型).代码中的 res{} 定义将 res(求和结果) 初始化为了 0.0,并在第33行到35行执行了真正的求和操作.我甚至使用访问者动态的修改了 variant 中的元素(代码第40行).

程序的输出如下.Visual C++ 中的运行时类型信息(std::type_info)给出了非常易读的类型名称.

 
   

C++17 基本完成,对于新特性大家怎么看? - 知乎 https://www.zhihu.com/question/56943731


C++17 的特性探索
https://alexiachen.github.io/blog/2017/08/31/explore-cpp17/

  1. 前言
  2. Optional
  3. Variant
  4. Any
  5. std::string_view
  6. std::invoke
  7. std::apply
  8. 类模版参数推导
  9. 用auto声明无类型的模版参数
  10. Folding表达式
  11. 在花括号初始化列表中的auto推导的新规则
  12. constexpr的lambda表达式
  13. lambda以值方式捕获this指针
  14. 内联变量
  15. 嵌套namespace
  16. Structured bindings
  17. constexpr if
  18. UTF-8字符字面量




随笔分类 - C++

C++17尝鲜:变长 using 声明
 

posted @ 2018-07-03 01:43 zwvista 阅读 (174) |  评论 (0)编辑

C++17尝鲜:编译期 if 语句
 

posted @ 2018-06-28 13:44 zwvista 阅读 (325) |  评论 (0)编辑

C++17尝鲜:variant
 

posted @ 2018-06-28 09:46 zwvista 阅读 (441) |  评论 (0)编辑

C++17尝鲜:string_view
 

posted @ 2018-06-27 12:57 zwvista 阅读 (1623) |  评论 (0)编辑

Boost.Hana
 

posted @ 2017-11-23 20:12 zwvista 阅读 (228) |  评论 (0)编辑

C++17尝鲜的更多相关文章

  1. C++17尝鲜:变长 using 声明

    using 声明 先来看 using 声明在类中的应用: 代码1 #include <iostream> using namespace std; struct A { void f(in ...

  2. C++17尝鲜:编译期 if 语句

    Constexpr If(编译期 if 语句) 以 if constexpr 打头的 if 语句被称为 Constexpr If. Constexpr If 是C++17所引入的新的语法特性.它为C+ ...

  3. C++17尝鲜:variant

    variant variant 是 C++17 所提供的变体类型.variant<X, Y, Z> 是可存放 X, Y, Z 这三种类型数据的变体类型. 与C语言中传统的 union 类型 ...

  4. C++17尝鲜:string_view

    string_view string_view 是C++17所提供的用于处理只读字符串的轻量对象.这里后缀 view 的意思是只读的视图. 通过调用 string_view 构造器可将字符串转换为 s ...

  5. C++17尝鲜:结构化绑定声明(Structured Binding Declaration)

    结构化绑定声明 结构化绑定声明,是指在一次声明中同时引入多个变量,同时绑定初始化表达式的各个子对象的语法形式. 结构化绑定声明使用auto来声明多个变量,所有变量都必须用中括号括起来. cv-auto ...

  6. C++17尝鲜:类模板中的模板参数自动推导

    模板参数自动推导 在C++17之前,类模板构造器的模板参数是不能像函数模板的模板参数那样被自动推导的,比如我们无法写 std::pair a{1, "a"s}; // C++17 ...

  7. C++17尝鲜:在 if 和 switch 语句中进行初始化

    初始化语句 在C++17中,类似于 for 语句,在 if 和 switch 语句的判断条件之前也能加上初始化语句,语法形式如下: if (初始化语句; 条件) 语句 else 语句 switch ( ...

  8. 带你尝鲜LiteOS 组件EasyFlash

    摘要:EasyFlash是一个开源的轻量级嵌入式闪存库. 本文分享自华为云社区<LiteOS组件尝鲜-玩转EasyFlash>,作者:Lionlace . 基本介绍 EasyFlash是一 ...

  9. JEP解读与尝鲜系列4 - Java 16 中对于 Project Valhalla 的铺垫

    这是 JEP 解读与尝鲜系列的第 4 篇,之前的文章如下: JEP解读与尝鲜系列 1 - Java Valhalla与Java Inline class JEP解读与尝鲜系列 2 - JEP 142 ...

随机推荐

  1. xcode静态库调试

    [工程1]:静态库工程,用来生成xxx.a [工程2]:项目工程,需要引入静态库xxx.a 工程2引入静态库的方法是将工程1生成的xxx.a和include头文件目录,加入到工程2中. 而如果需要在工 ...

  2. 58同城笔试题:数组去重;分饼干(分糖果);最小路径和(leetcode64)

    1. 数组去重 题目描述 /** * 有序数组去重 * 输出最终的数字个数 * 输入:1,2,2 * 输出:2 * @author Turing * */ 代码 import java.util.*; ...

  3. python运维开发常用模块(8)EXCEL操作模块XlsxWriter

    1.excel介绍 Excel是当今最流行的电子表格处理软件,支持丰富的计算函数及 图表,在系统运营方面广泛用于运营数据报表,比如业务质量.资源利 用.安全扫描等报表,同时也是应用系统常见的文件导出格 ...

  4. Azure EA (2) 使用Postman访问国内Azure Billing API

    <Windows Azure Platform 系列文章目录> 本文介绍的是国内由世纪互联运维的Azure China 请读者先看一下之前的文档内容:Azure EA (1) 查看国内Az ...

  5. torch_12_BigGAN全文解读

    1.摘要: 尽管近来生成图片模型取得了进步,成功生成了高分辨率的图片,但是在复杂的数据集中,样本的多样性仍然是难以捉摸的目标.本文尝试在大规模上训练生成对抗网络,并研究这种规模下的不稳定性.我们发现将 ...

  6. Ubuntu关机重启后 NVIDIA-SMI 命令不能使用

    问题: 电脑安装好Ubuntu系统后,后续安装了显卡驱动.CUDA.cuDNN等软件,后续一直没有关机.中间系统曾经有过升级,这也是问题所在.系统升级导致内核改变,并可能导致它与显卡驱动不再匹配,所以 ...

  7. 写一个操作 .ini文件的类

    class IniHelp { private string iniPath; [DllImport("kernel32")] private static extern long ...

  8. SSO单点登录和CAS

    一.单点登录流程 =====客户端====== 1.拦截客户端的请求判断是否有局部的session 2.1如果有局部的session,放行请求. 2.2如果没有局部session 2.2.1请求中有携 ...

  9. oracle 创建表、删除表、添加字段、删除字段、表备注、字段备注、修改表属性

    1.创建表 create table 表名( classid number() primary key, 表字段 数据类型 是否允许为空(not null:不为空/null:允许空) 默认值(defa ...

  10. C#关键字 const与readonly

    ====const==== const关键字来声明某个常量字段或常量局部变量.常量字段和常量局部变量不是变量而且不能修改.常量可以为数字.布尔值.字符串或null引用. 常数声明的类型指定声明引入的成 ...