南昌网站设计公司,海南营销网站建设,wordpress轻论坛主题,网站建设编码相关系列文章 C三剑客之std::any(一) : 使用 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)#xff1a;深入剖析 目录
1.概述
2.构建方式
2.1.构造函数
2.2.std::make_any
2.3.operator分配新值
3.访问值… 相关系列文章 C三剑客之std::any(一) : 使用 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)深入剖析 目录
1.概述
2.构建方式
2.1.构造函数
2.2.std::make_any
2.3.operator分配新值
3.访问值std::any_cast
4.修改器
4.1.emplace
4.2.reset
4.3.swap
5.观察器
5.1.has_value
5.2.type
6.总结 1.概述 C17的三剑客分别是std::optional, std::any, std::vairant。今天主要讲std::any。std::any类用于任何可拷贝构造类型的单个值的类型安全容器。在头文件any中c标准库定义了类std::any。
namespace std {
class any;
} 从上面的定义可以看出std::any不是模版类而是一种很特殊的容器它只能容纳一个元素但这个元素可以是任意的类型可以是基本数据类型intdoublecharfloat...也可以是复合数据类型类、结构体。 std: any是一种值类型它能够更改其类型同时仍然具有类型安全性。也就是说对象可以保存任意类型的值但是它们知道当前保存的值是哪种类型。在声明此类型的对象时不需要指定可能的类型。
2.构建方式
2.1.构造函数
默认情况下std::any的初始值为空。如
std::any a;
也可以赋初值初始化对象std::any, 如
std::any a 32; //type: int
std::any b wegrthweg; type : const chr*
要保存与初始值类型不同的类型必须使用in_place_type标记
std::any a{std::in_place_typeint, 420};
std::any b{std::in_place_typestd::string, asdfbsrghtr34};
即使传递给in_place_type的类型也会退化。下面的声明包含一个const char*:
std::any a{std::in_place_typeconst char[6], 12345};
要通过多个参数初始化可选对象必须创建该对象或将std::in_place_type添加为第一个参数(不能推断包含的类型):
std::any a1{std::complex{6.0, 2.0}};
std::any a2{std::in_place_typestd::complexdouble, 6.0, 2.0};
甚至可以传递一个初始化器列表后面跟着附加的参数:
auto func [] (int x, int y) { return std::abs(x) std::abs(y);};std::any a{std::in_place_typestd::setint,decltype(func), {3, 7, -1, -16, 1, 100}, func};
2.2.std::make_any
std::make_any的定义如下
//[1]
template class T, class... Args
std::any make_any( Args... args );
//[2]
template class T, class U, class... Args
std::any make_any( std::initializer_listU il, Args... args );
构造含 T 类型对象的 any 对象传递提供的参数给 T 的构造函数。
1) 等价于 return std::any(std::in_place_typeT, std::forwardArgs(args)...);
2) 等价于 return std::any(std::in_place_typeT, il, std::forwardArgs(args)...);
std::make_any必须显式指定初始化的类型(如果只传递一个参数则不会推导出初始化的类型)如
#include any
#include complex
#include functional
#include iostream
#include stringint main()
{auto a0 std::make_anystd::string(Hello, std::any!\n);auto a1 std::make_anystd::complexdouble(0.1, 2.3);std::cout std::any_caststd::string(a0);std::cout std::any_caststd::complexdouble(a1) \n;using lambda std::functionvoid(void);// 把 lambda 放入 std::any。尝试 #1 失败。std::any a2 [] { std::cout Lambda #1.\n; };std::cout a2.type() \ a2.type().name() \\n;// any_cast 转型到 void(void) 但实际类型不是// std::function ……而是 ~ main::{lambda()#1} 且它对// 每个 lambda 唯一。所以这会抛出……try {std::any_castlambda(a2)();}catch (std::bad_any_cast const ex) {std::cout ex.what() \n;}// 将 lambda 放入 std::any 中。尝试 #2 成功auto a3 std::make_anylambda([] { std::cout Lambda #2.\n; });std::cout a3.type() \ a3.type().name() \\n;std::any_castlambda(a3)();
}
输出
Hello, std::any!
(0.1,2.3)
a2.type() Z4mainEUlvE_
bad any_cast
a3.type() St8functionIFvvEE
Lambda #2.
2.3.operator分配新值
operator的定义如下
//[1]
any operator( const any rhs );
//[2]
any operator( any rhs ) noexcept;
//[3]
templatetypename ValueType
any operator( ValueType rhs );1) 通过复制 rhs 的状态赋值如同用 any(rhs).swap(*this)。
2) 通过移动 rhs 的状态赋值如同用 any(std::move(rhs)).swap(*this)。赋值后 rhs 留在有效但未指定的状态。
3) 以 rhs 的类型和值赋值如同用 any(std::forwardValueType(rhs)).swap(*this)。此重载只有在 std::decay_tValueType 与 any 不是同一类型且 std::is_copy_constructible_vstd::decay_tValueType 为 true 时才会参与重载决议。std::decay_tValueType 必须满足可复制构造条件。示例如下
std::any a 1223;
std::any b 2222222;
a b; //[1]
b 6.34655; //[3]
a std::make_anystd::complex(6.0, 2.0); //[2]上面代码执行a b调用的是第1个赋值函数b6.34655调用的是第3个赋值函数a std::make_anystd::complex(6.0, 2.0) 是调用的第2个赋值函数。
3.访问值std::any_cast
std::any_cast的定义
//[1]
template class T
T any_cast( const any operand );
//[2]
template class T
T any_cast( any operand );
//[3]
template class T
T any_cast( any operand );
//[4]
template class T
const T* any_cast( const any* operand ) noexcept;
//[5]
template class T
T* any_cast( any* operand ) noexcept;
要访问包含的值必须使用std::any_cast将其转换为其类型。将该值转换为一个字符串有几个选项:
std::any_caststd::string(a) // yield copy of the value
std::any_caststd::string(a); // write value by reference
std::any_castconst std::string(a); // read-access by reference
在这里如果转换失败将抛出std::bad_any_cast异常。因此在不检查或不知道类型的情况下最好实现以下功能:
try {auto s std::any_caststd::string(a);...
}
catch (std::bad_any_cast e) {std::cerr EXCEPTION: e.what() \n;
}
注意std::any_cast创建了一个传递类型的对象。如果将std::string作为模板参数传递给std::any_cast它将创建一个临时string(一个prvalue)然后用它初始化新对象。如果没有这样的初始化通常最好转换为引用类型以避免创建临时对象:
std::cout std::any_castconst std::string(a);
要修改该值需要转换为对应的引用类型:
std::any_caststd::string(a) 2134y56;
根据上面的第4或5个转换函数定义还可以为std::any对象的地址调用std::any_cast。在这种情况下如果类型匹配则强制转换返回相应的地址指针;如果不匹配则返回nullptr:
auto p std::any_caststd::string(a);
if (p) {...
}
下面再看一个例子
#include any
#include iostream
#include string
#include type_traits
#include utilityint main()
{// 简单示例auto a1 std::any(12);std::cout 1) a1 是 int std::any_castint(a1) \n;try{auto s std::any_caststd::string(a1); // 抛出}catch (const std::bad_any_cast e){std::cout 2) e.what() \n;}// 指针示例if (int* i std::any_castint(a1))std::cout 3) a1 是 int *i \n;else if (std::string* s std::any_caststd::string(a1))std::cout 3) a1 是 std::string *s \n;elsestd::cout 3) a1 是另一类型或者没有设置\n;// 进阶示例a1 std::string(hello);auto ra std::any_caststd::string(a1); // 引用ra[1] o;std::cout 4) a1 是字符串 std::any_caststd::string const(a1) \n; // const 引用auto s1 std::any_caststd::string(std::move(a1)); // 右值引用// 注意“s1” 是移动构造的 std::stringstatic_assert(std::is_same_vdecltype(s1), std::string);// 注意“a1” 中的 std::string 被置于合法但未指定的状态std::cout 5) a1.size() std::any_caststd::string(a1)-size() // 指针 \n 6) s1 s1 \n;
}
输出
1) a1 是 int12
2) bad any_cast
3) a1 是 int12
4) a1 是 stringhollo
5) a1.size()0
6) s1hollo
4.修改器
4.1.emplace
更改所含对象直接构造新对象示例如下
#include algorithm
#include any
#include iostream
#include string
#include vectorclass Star
{std::string name;int id;public:Star(std::string name, int id) : name { name }, id { id }{std::cout Star::Star(string, int)\n;}void print() const{std::cout Star{ \ name \ : id };\n;}
};auto main() - int
{std::any celestial;// (1) emplace( Args... args );celestial.emplaceStar(Procyon, 2943);const auto* star std::any_castStar(celestial);star-print();std::any av;// (2) emplace( std::initializer_listU il, Args... args );av.emplacestd::vectorchar({ C, , , 1, 7 } /* 无参数 */ );std::cout av.type().name() \n;const auto* va std::any_caststd::vectorchar(av);std::for_each(va-cbegin(), va-cend(), [](char const c) { std::cout c; });std::cout \n;
}
输出
Star::Star(string, int)
Star{ Procyon : 2943 };
St6vectorIcSaIcEE
C17
4.2.reset
销毁所含对象。
4.3.swap
交换两个std::any。
5.观察器
5.1.has_value
检查对象是否含有值若实例含值则为 true 否则为 false 。示例如下
#include any
#include iostream
#include stringint main()
{std::boolalpha(std::cout);std::any a0;std::cout a0.has_value(): a0.has_value() \n;std::any a1 42;std::cout a1.has_value(): a1.has_value() \n;std::cout a1 std::any_castint(a1) \n;a1.reset();std::cout a1.has_value(): a1.has_value() \n;auto a2 std::make_anystd::string(Milky Way);std::cout a2.has_value(): a2.has_value() \n;std::cout a2 \ std::any_caststd::string(a2) \\n;a2.reset();std::cout a2.has_value(): a2.has_value() \n;
}
输出
a0.has_value(): false
a1.has_value(): true
a1 42
a1.has_value(): false
a2.has_value(): true
a2 Milky Way
a2.has_value(): false
5.2.type
查询所含类型若实例非空则为所含值的 typeid 否则为 typeid(void) 。示例如下
#include type_traits
#include any
#include functional
#include iomanip
#include iostream
#include typeindex
#include typeinfo
#include unordered_map
#include vectortemplateclass T, class F
inline std::pairconst std::type_index, std::functionvoid(std::any const)to_any_visitor(F const f)
{return {std::type_index(typeid(T)),[g f](std::any const a){if constexpr (std::is_void_vT)g();elseg(std::any_castT const(a));}};
}static std::unordered_mapstd::type_index, std::functionvoid(std::any const)any_visitor {to_any_visitorvoid([]{ std::cout {}; }),to_any_visitorint([](int x){ std::cout x; }),to_any_visitorunsigned([](unsigned x){ std::cout x; }),to_any_visitorfloat([](float x){ std::cout x; }),to_any_visitordouble([](double x){ std::cout x; }),to_any_visitorchar const*([](char const *s){ std::cout std::quoted(s); }),// ……添加更多你的类型的特化……};inline void process(const std::any a)
{if (const auto it any_visitor.find(std::type_index(a.type()));it ! any_visitor.cend()) {it-second(a);} else {std::cout Unregistered type std::quoted(a.type().name());}
}templateclass T, class Finline void register_any_visitor(F const f)
{std::cout Register visitor for type std::quoted(typeid(T).name()) \n;any_visitor.insert(to_any_visitorT(f));
}auto main() - int
{std::vectorstd::any va { {}, 42, 123u, 3.14159f, 2.71828, C17, };std::cout { ;for (const std::any a : va) {process(a);std::cout , ;}std::cout }\n;process(std::any(0xFULL)); // 反注册 y 的类型 unsigned long long std::cout \n;register_any_visitorunsigned long long([](auto x) {std::cout std::hex std::showbase x; });process(std::any(0xFULL)); // OK 0xfstd::cout \n;
}
输出:
{ {}, 42, 123, 3.14159, 2.71828, C17, }
Unregistered type y
Register visitor for type y
0xf
6.总结 std::any是一个动态类型变量可以存储任何类型的值。它是由C17引入的一个新特性。std::any的设计目标是提供一种类型安全且易于使用的方式来在运行时处理各种类型的数据因为任何错误的类型转换都会在运行时抛出异常。然而std::any也有一些缺点。首先因为std::any在运行时并不知道它存储的数据的具体类型所以我们需要显式地进行类型转换。这可能会使代码变得复杂和难以理解。其次std::any的性能可能不如其他类型因为它需要在运行时进行类型检查和类型转换。 总之只要掌握了这些std::any的特性明白了它的使用场景才能灵活的使用std::any。
参考std::any - cppreference.com