可变参数模板类std::variant表示类型安全联合体(type-safe union)。std::variant的实例在任何给定时间要么保存其替代类型之一的值,要么在错误的情况下无值。
与union一样,如果std::variant保存某个对象类型T的值,则T的对象表示形式将直接在std::variant本身的对象表示形式中分配。不允许std::variant分配额外的(动态)内存,std::variant不会分配堆内存。
不允许std::variant保存引用、数组或void类型(references, arrays, or the type void)。空的std::variant也是格式错误的。
允许std::variant多次保存相同类型,并保存相同类型的不同cv限定(cv-qualified versions)版本。
std::variant允许你以灵活且类型安全的方式处理多种数据类型。std::variant可以存储各种数据类型的值,包括基本类型(int、double等)、用户定义类型(自定义类或结构),甚至其它std::variant。
使用std::in_place_type或std::in_place_index可对多个值初始化,也可以使用它们在初始化时解决歧义问题。
和std::optional、std::any一样,std::variant对象是值语义。也就是说,拷贝被实现为深拷贝。
std::variant持有的值有若干候选项(alternative),这些选项通常有不同的类型。然而,两个不同选项的类型也有可能相同。
初始化和赋值操作都会查找最匹配的选项。如果类型不能精确匹配,可能会发生奇怪的事情。std::variant没有空的状态,这意味着每一个构造好的std::variant对象,至少调用了一次构造函数。默认构造函数会调用第一个选项类型的默认构造函数。如果第一个类型没有默认构造函数,那么调用std::variant的默认构造函数将会导致编译期错误。结构体std::monostate可以处理这种情况。std::monostate可以作为第一个选项类型来保证std::variant能默认构造。
namespace {
template
auto& operator<<(std::ostream& out, const std::vector& v)
{
out << "{";
for (const auto& e : v)
out << e << " ";
return out << "}";
}
struct S {
S(int i) : i(i) {}
int i;
};
class Derived : public std::variant { };
} // namespace
int test_variant_init()
{
std::variant var1;
std::cout << "index: " << var1.index() << ", value: " << std::get(var1) << "\n"; // index: 0, value: 0
std::variant var2{ "CHINA" };
std::cout << "index: " << var2.index() << ", value: " << std::get(var2) << "\n"; // index: 0, value: CHINA
std::variant var3{ 66 };
std::cout << "index: " << var3.index() << ", value: " << std::get(var3) << "\n"; // index: 1, value: 66
std::variant, std::string, float> var4{ std::in_place_type, "CHINA" };
std::cout << "index: " << var4.index() << ", value: " << std::get(var4) << "\n"; // index: 1, value: CHINA
std::variant, float> var5{ std::in_place_type>, {1, 2, 3, 4, 5} };
std::cout << "index: " << var5.index() << ", value: " << std::get>(var5) << "\n"; // index: 1, value: {1 2 3 4 5 }
std::variant> var6{ std::in_place_index<2>, 5, "hi" };
std::cout << "index: " << var6.index() << ", value: " << std::get>(var6) << "\n"; // index: 2, value: {hi hi hi hi hi }
//std::variant var7; // error C2512: "std::variant<`anonymous-namespace'::S>::variant": 没有合适的默认构造函数可用
std::variant var7;
std::cout << "index: " << var7.index() << "\n"; // index: 0
if (std::holds_alternative(var7))
std::cout << "var7 has monostate\n"; // var7 has monostate
var7 = std::monostate{};
return 0;
}
std::variant相关函数:
(1).index:返回存储在std::variant中的数据类型从零开始的索引;
(2).emplace: 就地(in place)构造std::variant的值;若已存储值,则首先销毁当前存储的值,然后再初始化;
(3).holds_alternative: 检查std::variant当前是否拥有给定类型;返回bool类型;
(4).get:从std::variant中检索给定索引或类型(如果类型是唯一的)的值,如果不匹配,则抛出异常;返回存储值的引用;
(5).get_if:获取指向给定索引或类型(如果唯一)的std::variant值的指针,出错时返回nullptr,它的参数是一个指针;
(6).valueless_by_exception:检查std::variant是否处于无效状态;
(7).std::variant_npos:std::variant处于无效状态时,std::variant的索引值;
(8).std::swap:交换两个std::variant对象的值;
(9).operator==, !=, <, <=, >, >=:比较两个std::variant对象;
std::variant也支持move语义。
int test_variant_functions()
{
std::variant var1{ 66.6f };
std::cout << "index: " << var1.index() << ", value: " << std::get(var1) << "\n"; // index: 1, value: 66.6
var1.emplace<2>("CHINA");
std::cout << "index: " << var1.index() << ", value: " << std::get(var1) << "\n"; // index: 2, value: CHINA
var1.emplace<0>(88);
std::cout << "index: " << var1.index() << ", value: " << std::get(var1) << "\n"; // index: 0, value: 88
std::cout << std::boolalpha
<< "holds int: " << std::holds_alternative(var1)
<< ", holds string: " << std::holds_alternative(var1) << "\n"; // holds int: true, holds string: false
if (std::holds_alternative(var1))
std::cout << "var1 type: int\n"; // var1 type : int
std::variant> var2;
var2 = std::get(var1);
std::cout << "index: " << var2.index() << ", value: " << std::get(var2) << "\n"; // index: 1, value: 88
var2 = std::get<0>(var1); // <==> var2 = std::get(var1);
std::cout << "index: " << var2.index() << ", value: " << std::get(var2) << "\n"; // index: 1, value: 88
std::get<0>(var1) = 99;
std::cout << "index: " << var1.index() << ", value: " << std::get(var1) << "\n"; // index: 0, value: 99
try {
//var2 = std::get<2>(var1);
var2 = std::get(var1);
} catch (std::bad_variant_access const& ex) {
std::cout << "exception: " << ex.what() << "\n"; // windows: exception: bad variant access; linux: exception: std::get:wrong index for variant
}
std::variant var3;
std::get<0>(var3) = "China";
std::cout << "index: " << var3.index() << ", value: " << std::get<0>(var3) << "\n"; // index: 0, value: China
var3.emplace<1>("Beijing");
std::cout << "index: " << var3.index() << ", value: " << std::get<1>(var3) << "\n"; // index: 1, value: Beijing
auto check_value = [](const std::variant& v) {
if (const int* pval = std::get_if(&v))
std::cout << "variant value: " << *pval << '\n';
else
std::cout << "failed to get value" << '\n';
};
std::variant var4{ 12 }, var5{ 3.f };
check_value(var4); // variant value: 12
check_value(var5); // failed to get value
Derived var6{ "Beijing" };
std::cout << "index:" << var6.index() << ", value: " << std::get<1>(var6) << "\n"; // index:1, value: Beijing
std::swap(var4, var5);
std::cout << "var4 index: " << var4.index() << ", value: " << std::get<1>(var4) << "\n"; // var4 index: 1, value: 3
std::cout << "var5 index: " << var5.index() << ", value: " << std::get<0>(var5) << "\n"; // var5 index: 0, value: 12
std::cout << std::boolalpha << "var4 == var5: " << (var4 == var5) << "\n"; // var4 == var5: false
std::variant var7{ std::in_place_index<1>, "China" }, var8;
std::cout << "index: " << var7.index() << ", value: " << std::get<1>(var7) << "\n"; // index: 1, value: China
var7.emplace<0>("Bejing");
std::cout << "index: " << var7.index() << ", value: " << std::get<0>(var7) << "\n"; // index: 0, value: Bejing
var8 = std::move(var7);
std::cout << "index: " << var7.index() << ", value: " << std::get<0>(var7) << "\n"; // index: 0, value:
std::cout << "index: " << var8.index() << ", value: " << std::get<0>(var8) << "\n"; // index: 0, value: Bejing
return 0;
}
执行结果如下图所示:
GitHub:https://github.com/fengbingchun/Messy_Test