目录
- 1. 使用std::any
- 2. std::any类型和操作
- 2.1 std::any的类型
- 2.2 std::any操作
一般来说,c++是一种具有类型绑定和类型安全性的语言。值对象声明为具有特定类型,该类型定义哪些操作是可能的以及它们的行为方式。值对象不能改变它们的类型。
std: any是一种值类型,它能够更改其类型,同时仍然具有类型安全性。也就是说,对象可以保存任意类型的值,但是它们知道当前保存的值是哪种类型。在声明此类型的对象时,不需要指定可能的类型。
诀窍在于,对象同时拥有包含的值和使用typeid包含值的类型。因为这个值可以有任何大小,所以可以在堆上分配内存,鼓励实现避免小对象的动态分配。也就是说,如果分配一个字符串,对象将为该值分配内存并复制该字符串,同时也在内部存储分配的字符串。稍后,可以执行运行时检查来确定当前值的类型,并使用any_cast<该值的类型>获取值。
1. 使用std::any
下面的例子演示了std::any:
std::any a; // a is empty std::any b = 4.3; // b has value 4.3 of type double a = 42; // a has value 42 of type int b = std::string{"hi"}; // b has value "hi" of type std::string if (a.type() == typeid(std::string)) { std::string s = std::any_cast<std::string>(a); usestring(s); } else if (a.type() == typeid(int)) { useint(std::any_cast<int>(a)); }
可以声明std::any为空或由特定类型的值初始化。初始值的类型成为所包含值的类型。通过使用成员函数type(),可以根据任何类型的类型id检查所包含值的类型。如果对象是空的,对象类型id是typeid(void)。要访问包含的值,可以通过std::any_cast<对象类型>的方式:
auto s = std::any_cast<std::string>(a);
如果转换失败,因为对象为空或包含的类型不匹配,则抛出std::bad_any_cast。因此,在不检查或不知道类型的情况下,最好实现以下功能:
try { auto s = std::any_cast<std::string>(a); ... } catch (std::bad_any_cast& e) { std::cerr << "exception: " << e.what() << 'n'; }
注意,std::any_cast<>创建了一个传递类型的对象。如果将std::string作为模板参数传递给std::any_cast<>,它将创建一个临时string(一个prvalue),然后用它初始化新对象s。如果没有这样的初始化,通常最好转换为引用类型,以避免创建临时对象:
std::cout << std::any_cast<const std::string&>(a);
要修改该值,需要转换为对应的引用类型:
std::any_cast<std::string&>(a) = "world";
还可以为std::any对象的地址调用std::any_cast。在这种情况下,如果类型匹配,则强制转换返回相应的地址指针;如果不匹配,则返回nullptr:
auto p = std::any_cast<std::string>(&a); if (p) { ... }
例1:
#include <iostream> #include <any> int main() { std::any i = 42; const auto ptr = std::any_cast<int>(&i); if (ptr) { std::cout << ptr << std::endl; } return 0; }
结果如下:
要清空现有std::任何可以调用的对象:
方法1:a.reset(); // makes it empty
方法2:a = std::any{};
方法3: a = {};
可以直接检查对象是否为空:
if (a.has_value()) { ... }
还要注意,值是使用衰减类型存储的(数组转换为指针,忽略顶层引用和const)。对于字符串常量,这意味着值类型是const char*。要检查type()并使用std::any_cast<>,必须使用以下类型:
std::any a = "hello"; // type() is const char* if (a.type() == typeid(const char*)) { // true ... } if (a.type() == typeid(std::string)) { // false ... } std::cout << std::any_cast<const char*>(v[1]) << 'n'; // ok std::cout << std::any_cast<std::string>(v[1]) << 'n'; // exception
std::any没有定义比较运算符(因此,不能比较或排序对象),没有定义hash函数,也没有定义value()成员函数。由于类型只在运行时才知道,所以不能使用泛型lambdas处理与类型无关的当前值。总是需要运行时函数std::any_cast<>来处理当前值。
然而,可以将std::任何对象放入容器中。
例2:
#include <iostream> #include <vector> #include <any> int main() { std::vector<std::any> v; v.push_back(42); std::string s = "hello"; v.push_back(s); for (const auto& a : v) { if (a.type() == typeid(std::string)) { std::cout << "string: " << std::any_cast<const std::string&>(a) << 'n'; } else if (a.type() == typeid(int)) { std::cout << "int: " << std::any_cast<int>(a) << 'n'; } } }
结果如下:
2. std::any类型和操作
本节详细描述std::any的类型和操作。
2.1 std::any的类型
在头文件<any>中,c++标准库定义了类std::any,如下所示:
namespace std { class any; }
也就是说,std::any根本不是类模板。
此外,定义了以下类型和对象:
如果类型转换失败,则抛出异常类型std::bad_any_cast,它派生自std::bad_cast,而std::bad_cast派生自std::exception。
any对象还可以使用<utility>中定义的对象std::in_place_type(类型为std::in_place_type_t)。
2.2 std::any操作
std::any操作
函数 | 说明 |
constructors | 创建一个any对象(可能调用底层类型的构造函数) |
make_any() | 创建一个any对象(传递值来初始化它) |
destructor | 销毁any对象 |
= | 分配一个新值 |
emplace<t>() | 分配一个类型为t的新值 |
reset() | 销毁any对象的值(使对象为空) |
has_value() | 返回对象是否具有值 |
type() | 返回当前类型为std::type_info对象 |
any_cast<t>() | 使用当前值作为类型t的值(如果其他类型除外) |
swap() | 交换两个any对象的值 |
1. 构造函数
默认情况下,std::any的初始值为空。
std::any a1; // a1 is empty
如果传递一个值进行初始化,则将其衰减类型用作所包含值的类型:
std::any a2 = 42; // a2 contains value of type int std::any a3 = "hello"; // a2 contains value of type const char*
要保存与初始值类型不同的类型,必须使用in_place_type标记:
std::any a4{std::in_place_type<long>, 42}; std::any a5{std::in_place_type<std::string>, "hello"};
即使传递给in_place_type的类型也会衰减。下面的声明包含一个const char*:
std::any a5b{std::in_place_type<const char[6]>, "hello"};
要通过多个参数初始化可选对象,必须创建该对象或将std::in_place_type添加为第一个参数(不能推断包含的类型):
std::any a6{std::complex{3.0, 4.0}}; std::any a7{std::in_place_type<std::complex<double>>, 3.0, 4.0};
甚至可以传递一个初始化器列表,后面跟着附加的参数:
// initialize a std::any with a set with lambda as sorting criterion: auto sc = [] (int x, int y) { return std::abs(x) < std::abs(y);}; std::any a8{std::in_place_type<std::set<int,decltype(sc)>>, {4, 8, -7, -2, 0, 5}, sc};
注意,还有一个方便的函数make_any<>(),它可以用于单个或多个参数(不需要in_place_type参数)。必须显式指定初始化的类型(如果只传递一个参数,则不会推导出初始化的类型):
auto a10 = std::make_any<float>(3.0); auto a11 = std::make_any<std::string>("hello"); auto a13 = std::make_any<std::complex<double>>(3.0, 4.0); auto a14 = std::make_any<std::set<int,decltype(sc)>>({4, 8, -7, -2, 0, 5}, sc);
2. 访问值
要访问包含的值,必须使用std::any_cast<>将其转换为其类型。将该值转换为一个字符串,有几个选项:
std::any_cast<std::string>(a) // yield copy of the value std::any_cast<std::string&>(a); // write value by reference std::any_cast<const std::string&>(a); // read-access by reference
在这里,如果转换失败,将抛出std::bad_any_cast异常。
如果把std::any中所包含的类型转换为移除了传递类型的顶层引用后的类型id,则转换类型是适合的。如下:
#include <iostream> #include <string> #include <any> int main() { const auto& s = std::make_any<std::string>("hello"); if (s.type() == typeid(std::string))//删除顶层cosnt和引用后的类型 { auto a = std::any_cast<std::string>(s); std::cout << a << std::endl; } return 0; }
结果如下:
如果类型不匹配转换失败了,传递一个地址将会返回nullptr:
auto a = std::make_any<std::string>("hello"); std::any_cast<std::string>(&a) // write-access via pointer std::any_cast<const std::string>(&a); // read-access via pointer
注意,这里转换到引用会导致运行时错误:
std::any_cast<std::string&>(&a); // run-time error
3. 修改值
相应的赋值和emplace()操作。例如:
#include <iostream> #include <string> #include <any> #include <complex> int main() { std::any a; a = 42; // a contains value of type int a = "hello"; // a contains value of type const char* a.emplace<std::string>("hello world");// a contains value of type std::string return 0; }
结果如下:
4. 移动语法
std: any也支持移动语义。但是,请注意,move语义必须满足包含的类型具有可复制构造函数。也就是说,不支持只移动类型作为包含值类型。处理move语义的最佳方法可能并不明显。所以,你应该这样做:
std::string s("hello, world!"); std::any a; a = std::move(s); // move s into a s = std::move(std::any_cast<string&>(a)); // move assign string in a to s
与通常的从对象移动的情况一样,在最后一次调用之后,所包含的值a是未指定的。因此,可以使用a作为字符串,只要没有对所包含的字符串值的值做任何假设。
注意:
s = std::any_cast<string>(std::move(a));
也可以,但需要一个额外的移动。然而,以下内容是危险的(尽管它是c++标准中的一个例子):
std::any_cast<string&>(a) = std::move(s2); // oops: a to hold a string
只有当包含的值已经是字符串时,才可以这样做。如果没有,转换将抛出一个std::bad_any_cast异常。
到此这篇关于c++17之std::any的具体使用的文章就介绍到这了,更多相关c++17 std::any内容请搜索<计算机技术网(www.ctvol.com)!!>以前的文章或继续浏览下面的相关文章希望大家以后多多支持<计算机技术网(www.ctvol.com)!!>!
需要了解更多c/c++开发分享C++17之std::any的具体使用,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/1045441.html