C++17好用的类
C++17:std::any std::variant std::optional
简介
- std::any是一个类型安全的容器,可以放置各种类型的数据。
- std::variant是类型安全的union
- std::optional该类型是用来表示一个值是不是存在的。
std::any
场景:类型擦除,任意类型转化,类型安全
例子:
#include <any>
#include <iostream>
int main()
{
std::cout << std::boolalpha; //将bool值用 "true" 和 "false"显示
std::any a; //定义一个空的any,即一个空的容器
//有两种方法来判断一个any是否是空的
std << cout << a.has_value() <<std::endl; // any是空的时,has_value 返回值为 false
std << cout << a.type().name() <<std::endl; //any 是空的时,has_value 返回值为 true
//几种创建any的方式
std::any b = 1; //b 为存了int类型的值的any
auto c = std::make_any<float>(5.0f); //c为存了float类型的any
std::any d(6.0); //d为存储了double类型的any
std << cout << b.has_value() <<std::endl; //true
std << cout << b.type().name() <<std::endl; //int
std << cout << c.has_value() <<std::endl; //true
std << cout << c.type().name() <<std::endl; //float
std << cout << d.has_value() <<std::endl; //true
std << cout << d.type().name() <<std::endl; //double
//更改any的值
a = 2; //直接重新赋值
auto e = c.emplace<float>(4.0f); //调用emplace函数,e为新生成的对象引用
//清空any的值
b.reset();
std << cout << b.has_value() <<std::endl; //false
std << cout << b.type().name() <<std::endl; //int
//使用any的值
try
{
auto f = std::any_cast<int>(a); //f为int类型,其值为2
std::cout << f <<std::endl; //2
}
catch(const std::bad_any_cast& e)
{
std::cout<< e.what()<<std::endl;
}
try
{
auto g = std::any_cast<float>(a); //抛出std::bad_any_cat 异常
std::cout << g <<std::endl; //该语句不会执行
}
catch(const std::bad_any_cast& e)
{
std::cout<< e.what()<<std::endl; //可能输出Bad any_cast
}
return 0;
}
std::variant
场景:代替联合体,类型安全
#include <any>
#include <iostream>
#include <variant>
union my_union
{
int i;
float f;
char c;
};
int main()
{
std::cout << std::boolalpha;
std::variant<int, float, char> variant;
//这里的variant等价于my_union
//在构造的时候,如果构造过程中抛出了异常,valueless_by_exception的返回值为true
std::cout<< variant.valueless_by_exception()<<std::endl; //false
{
variant = 12; // variant包含了int类型
int i = std::get<int>(variant); //使用std::get<T>可以获取所含有的值
try
{
auto f = std::get<float>(variant); //此时的值为int,所以想要获取float的时候就会抛出异常
}
catch (const std::bad_variant_access& exception)
{
std::cout << exception.what() << std::endl;
}
variant = 1.0f;
auto f = std::get<float>(variant);
std::cout << f << std::endl; //1.0
}
{
//还可以使用索引来获取对应的值
auto f = std::get<1>(variant);
try
{
auto i = std::get<0>(variant);
}
catch (const std::bad_variant_access & exception)
{
std::cout << exception.what() << std::endl;
}
variant = 1;
auto i = std::get<0>(variant);
std::cout<<i<<std::endl; //1
}
variant = 2.0f;
std::cout << variant.index() << std::endl; //1
variant = 2;
std::cout << variant.index() << std::endl; //0
return 0;
}
std::optional
该类型是用来表示一个值是不是存在的。std::optional有两个状态,即有值和无值。通常我们将std::optional用于函数的返回值,当函数执行成功了返回有值的状态,当函数执行失败了返回无值的状态。当std::optional有值时,它可以在使用bool值的地方转化为true,反之,转化为false。
场景:
比如说有一个读取文件的函数,这个函数会把文件的内容,存储在一个string中,作为结果返回。因为读取可能会失败,所以这个函数有必要返回一个信息,来表明读取文件是否成功,如果用返回空字符串的方式来表示读取失败,不太好,因为文件可能本身就是空文件,所以空字符串不能代表读取失败。
例子:
#include <iostream>
#include <optional>
int main()
{
std::cout << std::boolalpha;
std::optional<int> op1; //表示一个不存在的值
std::optional<int> op2 = 1; //表示一个存在的int类型的值
std::optional<int> op3(2); //表示存在的一个int类型的值
std::optional<std::string> op4(std::in_place, 3, 'A'); //构建一个存有std::string类型的std::optional,
//调用std::string的构造函数,其值为"AAA"
std::cout << op1.has_value() << std::endl; //输出为false
if (!op1)
{
std::cout << "empty optional is false" << std::endl; //由于op1不存在值,所以在此转化为false
}
try
{
std::cout << op4.value() << std::endl; //获取std::optional 的值,如果不存在值,则抛出std::bad_optional_access异常
}
catch (std::bad_optional_access& e)
{
std::cout << e.what() << std::endl;
}
std::optional<int> op5;
std::cout<<op5.value_or(5)<<std::endl;
op2.reset();
std::cout << op2.has_value() << std::endl; //false
op2 = 3;
op2 = 4.0;
std::cout << op2.value() << std::endl; //4.0
op2.emplace<int>(5);
return 0;
}
总结
多使用C++标准库好用的工具