文章目录
- 前言与需求
- union
- 内存映射图
- C++11的union
- 使用
- ref示例
- 构造
- 普通构造
- 置空
- emplace
- monostate
- 访问
- std::get<>
- std::holds_alternative<>
- 获取指针std::get_if<>
- 获取可选数量个数std::variant_size
- END
前言与需求
联合体,是在C语言时代就存在的概念。主要应用在一些收内存限制较大的情景。
但是传统C的限制太大,但是到了C++中给出了更安全的类型std::variant
union
内存映射图
先来看一张C语言数据结构内存映射图
!网图(非原创),侵删!
在union中所有字段是公用一块内存区域的。
C++11的union
在旧时union中成员不能是一个非凡类型。到了C++11解除了限制,只要是非应用类型即可。
其实union也可以单独开一篇文章来讲,这里给个简单示例,不做过多讲解:
#include <iostream>
#include <string>
#include <vector>
union U {
U() {
}
~U() {
}
static int s;
int x;
float y;
std::string str;
std::vector<int> vec;
};
// 同类的静态成员类似
int U::s = 10;
int main() {
std::cout << U::s << std::endl;
U u;
// 使用 placement new 和 手动析构
new (&u.str) std::string("hello world");
std::cout << u.str << std::endl;
u.str.~basic_string();
new (&u.vec) std::vector<int>(2, -1);
u.vec.push_back(1);
for (int i = 0; i < u.vec.size(); i += 1) {
std::cout << u.vec[i] << std::endl;
}
u.vec.~vector();
return 0;
}
使用
std::variant - cppreference.com
ref示例
std::variant 是类型安全的,只能访问一种类型
#include <cassert>
#include <string>
#include <variant>
int main() {
std::variant<int, float> v, w;
v = 12; // v 含 int
int i = std::get<int>(v);
w = std::get<int>(v);
w = std::get<0>(v); // 与前一行效果相同
w = v; // 与前一行效果相同
// std::get<double>(v); // 错误: [int, float] 中无 double
// std::get<3>(v); // 错误:合法下标值为 0 与 1
try {
std::get<float>(w); // w 含 int 而非 float :将抛出
} catch (const std::bad_variant_access&) {
}
using namespace std::literals;
std::variant<std::string> x("abc"); // 转换构造函数在无歧义时起作用
x = "def"; // 转换赋值在无歧义时亦起作用
std::variant<std::string, void const*> y("abc");
// 传递 char const * 时转换成 void const *
assert(std::holds_alternative<void const*>(y)); // 成功
y = "xyz"s;
assert(std::holds_alternative<std::string>(y)); // 成功
}
构造
直接赋值即可。
普通构造
#include <iostream>
#include <string>
#include <variant>
void fun() {
// 可以相同
std::variant<int, int> var;
// 操作比较特殊
}
int main() {
std::variant<std::string, int> empty;
std::variant<std::string, int> var("abc");
std::cout << std::get<0>(var) << std::endl;
var = 123;
std::cout << std::get<1>(var) << std::endl;
std::get<1>(var) = 654321;
std::cout << std::get<1>(var) << std::endl;
}
置空
#include <iostream>
#include <string>
#include <variant>
int main() {
std::variant<int, std::string> v = "abc";
// v.index = 1
std::cout << "v.index = " << v.index() << '\n';
// 置空了
v = {};
// v.index = 0
std::cout << "v.index = " << v.index() << '\n';
}
emplace
可以原地构造
#include <iostream>
#include <string>
#include <variant>
int main() {
std::variant<std::string> var;
var.emplace<0>(2, 'a');
std::cout << std::get<0>(var) << std::endl;
var.emplace<std::string>("Hello World");
std::cout << std::get<std::string>(var) << std::endl;
}
monostate
有一类情况比较特殊。
有意为行为良好的 std::variant
中空可选项所用的单位类型。具体而言,非可默认构造的 variant
可以列 std::monostate
为其首个可选项:这使得 variant
自身可默认构造。
- std::monostate
- valueless_by_exception()
#include <iostream>
#include <variant>
struct S {
int value;
S(int i) : value(i) {
std::cout << __func__ << std::endl;
}
};
int main() {
// 若无 monostate 类型则此声明将失败。
// 这是因为 S 不可默认构造。
std::variant<std::monostate, S> var;
// 不保有值
std::cout << std::boolalpha << var.valueless_by_exception() << std::endl;
// std::get<S> 将抛异常!我等需要赋一个值
// var.index() 现为 0 ——首个元素
std::cout << "cur index = " << var.index() << '\n';
var = 12;
std::cout << std::get<S>(var).value << '\n';
}
访问
std::get<>
请注意同类型的情况
#include <iostream>
#include <string>
#include <variant>
void fun0() {
// 可以相同
std::variant<int, int> var;
// 编译不通过,非动态错误
// var = 10;
// 编译不通过,非动态错误
// std::get<int>(var) = 100;
// 下标访问可行
std::get<0>(var) = 10;
try {
std::cout << std::get<1>(var) << std::endl;
} catch (const std::bad_variant_access& e) {
// std::get: wrong index for variant
std::cout << e.what() << std::endl;
}
}
void fun1() {
std::variant<std::string, int> var("abc");
std::cout << std::get<0>(var) << std::endl;
std::cout << std::get<std::string>(var) << std::endl;
}
int main() {
fun0();
fun1();
}
std::holds_alternative<>
判断是否可以转换
#include <iostream>
#include <string>
#include <variant>
int main() {
std::cout << std::boolalpha;
std::variant<std::string, int> var("abc");
std::cout << "int " << std::holds_alternative<int>(var) << std::endl;
std::cout << "string " << std::holds_alternative<std::string>(var)
<< std::endl;
}
获取指针std::get_if<>
#include <iostream>
#include <variant>
int main() {
std::variant<int, float> var;
std::cout << &var << std::endl;
// 整形
var = 12;
auto pval = std::get_if<int>(&var);
std::cout << pval << std::endl;
if (pval) {
std::cout << "variant value: " << *pval << '\n';
} else {
std::cout << "failed to get value!" << '\n';
}
// 浮点数
var = 12.3f;
auto pval2 = std::get_if<float>(&var);
std::cout << pval2 << std::endl;
if (pval2) {
std::cout << "variant value: " << *pval2 << '\n';
} else {
std::cout << "failed to get value!" << '\n';
}
}
0xc10cbffba0
0xc10cbffba0
variant value: 12
0xc10cbffba0
variant value: 12.3
获取可选数量个数std::variant_size
在编译器确定
- std::variant_size
- std::variant_size_v
#include <any>
#include <cstdio>
#include <variant>
// 全部 pass
static_assert(std::variant_size_v<std::variant<>> == 0);
static_assert(std::variant_size_v<std::variant<int>> == 1);
static_assert(std::variant_size_v<std::variant<int, int>> == 2);
static_assert(std::variant_size_v<std::variant<int, int, int>> == 3);
static_assert(std::variant_size_v<std::variant<int, float, double>> == 3);
// std::monostate 也算一个占位
static_assert(std::variant_size_v<std::variant<std::monostate, void>> == 2);
static_assert(std::variant_size_v<std::variant<const int, const float>> == 2);
static_assert(std::variant_size_v<std::variant<std::variant<std::any>>> == 1);
int main() {
std::puts("All static assertions passed.");
}