std::variant
variant实现了union的作用
用法
创建
std::variant<type0,type1,type2,...> 名称
创建一个variant,其中值可以是给定type的任意一种
获取值
get<类型下标/类型名称>(名称)按对应下标的数据类型获取值,若不匹配则抛出异常
名称.index()获取当前值对应数据类型的索引
holds_alternative<类型>(名称)判断variant对象是否含有指定类型的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include<iostream> #include<variant>
int main(){ std::variant<std::string, int, double> v1; v1 = "hello";
try{ get<int>(v1); } catch(const std::bad_variant_access& e){ std::cout<<"Bad variant access"<<e.what()<<std::endl;
} if (0==v1.index()){ std::cout <<std::get<0>(v1)<<std::endl; } if (std::holds_alternative<std::string>(v1)){ std::cout << std::get<std::string>(v1)<<std::endl; } else{ std::cout <<"Not a String"<<std::endl; } return 0; }
|
std::visit
visit函数会根据variant中值的类型来调用自定义对象中对应的函数调用运算符重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<iostream> #include<variant>
struct visitor{ void operator()(int i) const{std::cout <<"int: "<<i<<std::endl;} void operator()(double d) const{std::cout <<"double: "<<d<<std::endl;} void operator()(const std::string &s) const{std::cout <<"string: "<<s<<std::endl;} };
int main(){ std::variant<int, double, std::string> v; v = 6; std::visit(visitor{}, v); v = "hello"; std::visit(visitor{}, v); }
|
overload pattern
我们可以使用overload模式使得variant使用更加方便
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include<iostream> #include<variant> struct A{ void operator()(int i) const{std::cout <<"int: "<<i<<std::endl;} }; struct B{ void operator()(double d) const{std::cout <<"double: "<<d<<std::endl;} }; struct C{ void operator()(const std::string &s) const{std::cout <<"string: "<<s<<std::endl;} };
template<class... Ts> struct Visitor:Ts...{ using Ts::operator()...; };
int main(){ std::variant<int, double, std::string> v; v = 6; Visitor<A,B,C> visitor{}; visitor(6); visitor("hello");
std::visit(visitor, v); auto a = [](int i){ std::cout <<"int: "<<i <<std::endl; }; auto b = [](double d){ std::cout <<"double: "<<d <<std::endl; }; auto c = [](const std::string &s){ std::cout <<"string: "<<s <<std::endl; };
v = "hello"; Visitor<decltype(a), decltype(b), decltype(c)> visitor2{a,b,c}; std::visit(visitor2, v);
v = 6.3; Visitor visitor3{a,b,c}; std::visit(visitor3, v); }
|
这里将visitor类模板进行改造
使用class... Ts表示实例化传入多个实参
使用Ts...将形参包展开
使用using Ts::operator()...将基类成员引入作为继承类成员定义,将所有基类中的重载函数引入成为自己的成员函数
以下使用了三种方法:
让Visitor类继承A,B,C三个模板,拥有三个运算符重载
定义三个lambda对象,使用decltype获得他们的类型,用来实例化Vistor模板
使用CTAD,让编译器从对象的初始化器列表来推断模板类型(C++17)
这时编译器会自动将类型推到为第二种方法中的类型,因此我们可以直接将三个lambda函数塞入Visitor函数让编译器自动推导
1 2 3 4 5 6 7 8
| std::visit(Visitor{ [](int i){ std::cout <<"int: "<<i <<std::endl;}, [](double d){ std::cout <<"double: "<<d <<std::endl;}, [](const std::string &s){ std::cout <<"string: "<<s <<std::endl;}}, v);
|
这样在C++20以上可以实现,C++17下需要用户自定义推导指引
即将初始化列表实参类型作为模板实参列表
1 2
| template<class... Ts> overload(Ts...) -> overload<Ts...>
|