C++ 17特性
C++ 的结构化绑定 (Structured Binding)
结构化绑定 struct bindin
g 是C++17的新特性,能让我们更好地处理多返回值。可以在将函数返回为tuple、pair、struct等结构时且赋值给另外变量的时候,直接得到成员,而不是结构。
对于多个返回值,之前是用结构体去处理,而这个结构化绑定就是在这个的基础上拓展的一种新方法,特别是处理元组,对组(pairs)以及返回诸如此类的东西。
用g++ 编译时需要加上 ‘-std=c++17’ or ‘-std=gnu++17’
老方法(tuple、pair)演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <string>
#include <tuple>
// std::pair<std::string,int> CreatPerson() // 只能有两个变量
std::tuple<std::string, int> CreatPerson() // 可以理解为pair的扩展
{
return {"Cherno", 24};
}
int main()
{
//元组的数据获取易读性差,还不如像结构体一样直接XXX.age访问更加可读。
// std::tuple<std::string, int> person = CreatPerson();
auto person = CreatPerson(); //用auto关键字
std::string& name = std::get<0>(person);
int age = std::get<1>(person);
//tie 可读性好一点
std::string name;
int age;
std::tie(name, age) = CreatPerson();
}
C++17 新方法:结构化绑定处理多返回值演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
#include <tuple>
std::tuple<std::string, int> CreatPerson()
{
return {"Cherno", 24};
}
int main()
{
auto[name, age] = CreatPerson(); //直接用name和age来储存返回值
std::cout << name;
}
std::optional
C++ 如何处理 optional 数据
C++17 在 STL 中引入了 std::optional
,就像 std::variant
一样,std::optional
是一个 “和类型 (sum type)“,也就是说,std::optional
类型的变量要么是一个 T
类型的变量,要么是一个表示 “ 什么都没有 “ 的状态。
std::optional
是 C++17 的新东西,用于检测数据是否存在 or 是否是我们期盼的形式,用于处理那些可能存在,也可能不存在的数据 or 一种我们不确定的类型 。
std::optional
提供的常见方法 :
- has_value():判断对应的optional 是否处于已经设置值的状态
1
2
3
4
5
6
7
8
9
10
11
int main()
{
std::string text = /*...*/;
std::optional<unsigned> opt = firstEvenNumberIn(text);
if (opt.has_value()) //直接if(opt)即可,代码更简洁
{
std::cout << "The first even number is "
<< opt.value()
<< ".\n";
}
}
- 访问 optional 对象中的数据
1
2
3
4
5
1. opt.value()
2. (*opt)
3. value_or() //value_or()可以允许传入一个默认值, 如果optional为std::nullopt,
//则直接返回传入的默认值.(如果数据确实存在于std::optional中,
//它将返回给我们那个字符串。如果不存在,它会返回我们传入的任何值)
示例:比如在读取文件内容的时候,往往需要判断读取是否成功,常用的方法是传入一个引用变量或者判断返回的 std::string 是否为空,C++17 引入了一个更好的方法,std::optional
老方法:传入一个引用变量或者判断返回的 std::string 是否为空
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <fstream>
#include <string>
std::string ReadFile(const std::string &fileapath, bool &outSuccess) {
std::ifstream stream(filepath);
//如果成功读取文件
if (stream) {
std::string result;
getline(stream,result);
stream.close();
outSuccess = true; //读取成功,修改bool
return result;
}
outSuccess = false; //反之
}
int main() {
bool flag;
auto data = ReadFile("data.txt", flag);
//如果文件有效,则接着操作
if (flag) {
}
}
新方法:std::optional:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 用g++编译时需要加上‘-std=c++17’ or ‘-std=gnu++17’
// std::optional同样是C++17的新特性,可以用来处理可能存在、也可能不存在的数据
//data.txt在项目目录中存在,且其中的内容为"data!"
#include <iostream>
#include <fstream>
#include <optional>
#include <string>
std::optional<std::string> ReadFileAsString(const std::string& filepath)
{
std::ifstream stream(filepath);
if (stream)
{
std::string result;
//getline(stream, result);
stream.close();
return result;
}
return {}; //返回空
}
int main()
{
std::optional<std::string> data = ReadFileAsString("data.txt");
std::string value = data.value_or("Not present");
std::cout << value << std::endl;
if (data)
{
std::cout << "File read successfully!" << std::endl;
}
else
{
std::cout << "File could not be opened!" << std::endl;
}
}
//输出
// Not present
// File could not be opened!
如果文件无法打开,或者文件的特定部分没有被设置或读取,也许我们有一个默认值,这很常见。此时就可以使用 value_or() 函数。其作用就是:如果数据确实存在于 std::optional 中,它将返回给我们那个字符串。如果不存在,它会返回我们传入的任何值。
删除 data.txt,此时文件不存在打不开,则被设置为默认值
std::variant
C++ 单一变量存放多种类型的数据
std::variant
是C++17
的新特性,可以让我们不用担心处理的确切数据类型,是一种可以容纳多种类型变量的结构。
它和
option
很像,它的作用是让我们不用担心处理确切的数据类型,只有一个变量,之后我们在考虑它的具体类型
故我们做的就是指定一个叫 std::variant
的东西,然后列出它可能的数据类型
- 与
union
的区别
1)union 中的成员内存共享。union 更有效率。 2) std::variant
的大小是 <>
里面的大小之和。variant更加类型安全,不会造成未定义行为,所以应当去使用它,除非做的是底层优化,非常需要性能。
示例:
1
2
3
4
5
6
7
8
9
std::variant<string, int> data; //列举出可能的类型
data = "hello";
// 索引的第一种方式:std::get,但是要与上一次赋值类型相同,不然会报错
cout << std::get<string>(data) <<endl;//print hello
data = 4;
cout << std::get<int>(data) <<endl;//print 4
cout << std::get<string>(data) <<endl;//编译通过,但是runtime会报错,显示std::bad_variant_access
data = false;//能编译通过
cout << std::get<bool>(data) <<endl;//这句编译失败
- index() 索引
1
2
3
std::variant<string, int> data; //列举出可能的类型
//std::variant的index函数
data.index();// 返回一个整数,代表data当前存储的数据的类型在<>里的序号,比如返回0代表存的是string, 返回1代表存的是int
- get_if()
1
2
3
4
5
6
// std::get的变种函数,get_if
auto p = std::get_if<std::string>(&data);//p是一个指针,如果data此时存的不是string类型的数据,则p为空指针,别忘了传的是地址
// 如果data存的数据是string类型的数据
if(auto p = std::get_if<string>(&data)){
string& s = *p;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//参考:https://zhuanlan.zhihu.com/p/352420950
#include<iostream>
#include<variant>
int main()
{
std::variant<std::string,int> data; // <>里面的类型不能重复
data = "ydc";
// 索引的第一种方式:std::get,但是要与上一次赋值类型相同,不然会报错
std::cout<<std::get<std::string>(data)<<std::endl;
// 索引的第二种方式,std::get_if,传入地址,返回为指针
if (auto value = std::get_if<std::string>(&data))
{
std::string& v = *value;
}
data = 2;
std::cout<<std::get<int>(data)<<std::endl;
std::cin.get();
}
std::any
C++ 如何存储任意类型的数据
- 是 C++17 引入的可以存储多种类型变量的结构,其本质是一个
union
,但是不像std::variant
那样需要列出类型。使用时要包含头文件#include <any>
- 对于小类型 (small type) 来说,any 将它们存储为一个严格对齐的
union
,对于大类型,会用void*
,动态分配内存 - 评价:基本无用。 当在一个变量里储存多个数据类型,用 any 的类型安全版本即可:
variant
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <any>
// 这里的new的函数,是为了设置一个断点,通过编译器观察主函数中何处调用了new,看其堆栈。
void *operator new(size_t size)
{
return malloc(size);
}
int main()
{
std::any data;
data = 2;
data = "Cherno";
data = std::string("Cherno");
std::string& string = std::any_cast<std::string&>(data); //用any_cast指定转换的类型,如果这个时候any不是想要转换的类型,则会抛出一个类型转换的异常
// 通过引用减少复制操作,以免影响性能
}