腾讯街景地图全景在线,静态网站怎么做优化,网站开发成本报表,网站建设策划书主要内容1简介
C17的三剑客分别是std::optional, std::any, std::vairant。今天主要讲std::variant。std::variant的定义如下#xff1a;
template class... Types
class variant;
类模板 std::variant 表示一个类型安全的联合体#xff08;以下称“变化体”#xff09;…1简介
C17的三剑客分别是std::optional, std::any, std::vairant。今天主要讲std::variant。std::variant的定义如下
template class... Types
class variant;
类模板 std::variant 表示一个类型安全的联合体以下称“变化体”。std::variant 的一个实例在任意时刻要么保有它的一个可选类型之一的值要么在错误情况下无值此状态难以达成见 valueless_by_exception从功能上讲它就跟union的功能差不多但却比union更高级variant主要是为了提供更安全的union。举个例子union里面不能有string这种类型但std::variant却可以还可以支持更多复杂类型如map等
2辅助类
2.1 std::monostate
为了支持第一个类型没有默认构造函数的variant对象提供了一个特殊的helper类型:std::monostate。类型std::monostate的对象总是具有相同的状态因此它们总是相等的。它自己的目的是表示另一种类型这样variant就没有任何其他类型的值。也就是说std::monostate可以作为第一种替代类型使变体类型默认为可构造。
std::variantstd::monostate, int test; // OK
std::cout index: test.index(); //输出 0
我们可以简单理解std::monostate是个占位符。
2.2 std::bad_variant_access std::bad_variant_access 是下列情形中抛出的异常类型
以不匹配当前活跃可选项的下标或类型调用 std::get(std::variant)调用 std::visit 观览因异常无值 (valueless_by_exception) 的 variant 示例如下
#include variant
#include iostreamint main()
{std::variantint, float v;v 12;try {std::getfloat(v);}catch(const std::bad_variant_access e) {std::cout e.what() \n;}
}
可能的输出
bad_variant_access
2.3 std::variant_size和std::variant_size_v
提供作为编译时常量表达式对可有 cv 限定的 variant 中可选项数量的访问示例如下
#include any
#include cstdio
#include variantstatic_assert(std::variant_size_vstd::variant 0);
static_assert(std::variant_size_vstd::variantint 1);
static_assert(std::variant_size_vstd::variantint, int 2);
static_assert(std::variant_size_vstd::variantint, int, int 3);
static_assert(std::variant_size_vstd::variantint, float, double 3);
static_assert(std::variant_size_vstd::variantstd::monostate, void 2);
static_assert(std::variant_size_vstd::variantconst int, const float 2);
static_assert(std::variant_size_vstd::variantstd::variantstd::any 1);int main() { std::puts(All static assertions passed.); }
输出
All static assertions passed.
2.4 std::variant_alternative和std::variant_alternative_t
提供对 variant 的类型编译时下标访问,示例如下
#include variant
#include iostreamusing my_variant std::variantint, float;
static_assert(std::is_same_vint, std::variant_alternative_t0, my_variant);
static_assert(std::is_same_vfloat, std::variant_alternative_t1, my_variant);
// variant 类型上的 cv 限定传播给提取出的可选项类型。
static_assert(std::is_same_vconst int, std::variant_alternative_t0, const my_variant);int main()
{std::cout All static assertions passed\n;
}
输出
All static assertions passed
3 std::variant的操作
std::variant提供的主要操作有
操作说明constructors创建一个variant对象(可能调用底层类型的构造函数)destructor销毁一个variant对象emplaceT()为具有类型T的备选项分配一个新值emplaceIdx()为索引Idx的备选项分配一个新值分配一个新值index()返回当前备选项的索引holds_alternativeT()返回类型T是否有值, !, , , , 比较variant对象swap()交换两个对象的值hash函数对象类型来计算哈希值valueless_by_exception()返回该变量是否由于异常而没有值getT()返回备选项类型为T的值或抛出异常(如果没有类型为T的值)getIdx()返回备选项索引为idx的值或抛出异常(如果没有索引为idx的值)get_ifT()返回指向类型为T指针或返回nullptr(如果没有类型为T的值)get_ifIdx()返回指向索引Idx的指针或nullpt(如果没有索引为idx的值)visit()为当前备选项执行操作 3.1 定义
直接定义std::variant,如
std::variantstd::uint32_t, double, std::string, std::int32_t t;
可以对 t 赋初值对于基本类型它是0、false还是nullptr。如果传递一个值进行初始化则使用最佳匹配类型如
std::variantstd::uint32_t, double, std::string, std::int32_t t{ 25 };
cout t.index(); //输出: 3
要传递多个值进行初始化必须使用in_place_type或in_place_index标记:
std::variantstd::complexdouble t1{1.0,564.0}; // ERROR
std::variantstd::complexdouble t2{std::in_place_typestd::complexdouble,
322.0, 2323.0};
std::variantstd::complexdouble t3{std::in_place_index0, 35.0, 8.0};
如果初始化过程中出现歧义或匹配问题可以用in_place_index标签来解决如
std::variantint, int t1{std::in_place_index1, 77}; // init 2nd int
std::variantint, long t2{std::in_place_index1, 77}; // init long, not int
std::cout t2.index(); // prints 1
3.2 访问值
std::variant可以通std::get来获取或修改值。
std::variantstd::uint32_t, double, std::string, std::int32_t var;
auto x std::getdouble(var);
auto y std::get4(var); // compile-time ERROR: no 4th alternative
auto c std::getint(var); try{auto m std::getstd::string(var); // throws exception (first int currently set)auto n std::get0(var); // OK, i0auto o std::get1(var); // throws exception (other int currently set)
}
catch (const std::bad_variant_access e) { // in case of an invalid accessstd::cout Exception: e.what() \n;
}
std::get_if用来判断某项是否存在
#include variant
#include iostreamint main()
{std::variantint, float v{12};if(auto pval std::get_ifint(v))std::cout variant value: *pval \n; //输出: 12else std::cout failed to get value! \n;
}
还可以通过std::visit来访问值
struct stValueVisitor {void operator()(int i) { cout int: i \n; }void operator()(float f) { cout float: f \n; }void operator()(const std::string s) { cout str: s \n; }
};int main() {std::variantint, float, string value 65.2std::visit(stValueVisitor{}, value); // 输出 float: 65.2return 0;}
也可以利用C17 新增的 overloaded 模板可以直接生成匿名访问器简化代码 下面的代码是等价的:
int main() {std::variantint, float, string t 56.4std::visit(overloaded{void operator()(int i) { cout int: i \n; }void operator()(float f) { cout float: f \n; }void operator()(const std::string s) { cout str: s \n; }},t);}
接下来还有第三种方法来访问std::variant :
int main()
{std::variantint, float, string t 16.4std::visit([](auto arg) {using T std::decay_tdecltype(arg); // 类型退化去掉类型中的const 以及 if constexpr(std::is_same_vT,int) {cout int: arg \n;} else if constexpr(std::is_same_vT,float){cout float: arg \n;} else if constexpr(std::is_same_vT,std::string){cout str: arg \n;}}, t);
}
这里我们可以看出来第三种写法比第一种的优势在哪里了编译期推断。 第三种方法由于使用了constexpr 进行 if 分支的判断因此是在编译期运行而第一种方法是运行期进行类型判断效率是不同的。 第二种方法和模板一样也是编译期推断的因此效率也是很高的所以我们应当尽量使用 std::visit 方法来访问variant 变量
另外std::visit 还有一个好处是它的参数列表是不定长的我们可以传入多个variant 变量
template class Visitor, class... Variants
constexpr visit(Visitor vis, Variant... vars);
3.3 修改值
用std::get来修改值如
std::variantstd::uint32_t, double, std::string, std::int32_t t;
std::getdouble(t) 1.0;
cout t.index(); //输出: 1
std::getstd::uint32_t(t) 34;
cout t.index(); //输出: 0
std::get2(t) 4343636;
cout t.index(); //输出:2
也可以用std::get_if来修改值如
std::variantstd::uint32_t, double, std::string, std::int32_t t;
if (auto p std::get_if1(t)) { // if second int set*p 42.2; // modify it
}
4 总结
以上都是我日常工作中对std::variant的用法的总结做技术要知其然更要知其所以然后面我将从std::varant的源码实现上继续分析它的原理敬请期待。。。 参考
std::visit
std::variant