C++17之std::visit的具体使用
作者:云洞
它们必须明确地为每种可能的类型提供函数调用操作符。然后,使用相应的重载来处理当前的备选项类型。
1. 使用对象函数方式访问
例1:
#include <iostream> #include <variant> #include <string> struct MyVisitor { void operator()(double d) const { std::cout << d << '\n'; } void operator()(int i) const { std::cout << i << '\n'; } void operator()(const std::string& s) const { std::cout << s << '\n'; } }; int main() { std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit"); std::visit(MyVisitor(), var1); // calls operator() for matching int type std::visit(MyVisitor(), var2); // calls operator() for matching double type std::visit(MyVisitor(), var3); // calls operator() for matching std::string type return 0; }
结果如下:
如果操作符()不支持所有可能的类型,或者调用不明确,则visit()调用是编译时错误。还可以使用访问者修改当前类型的值(但不能分配新类型的值)。
例2:
#include <iostream> #include <variant> #include <string> struct Twice { void operator()(double& d) const { d *= 2; } void operator()(int& i) const { i *= 2; } void operator()(std::string& s) const { s = s + s; } }; int main() { std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit"); std::visit(Twice(), var1); // calls operator() for matching int type std::visit(Twice(), var2); // calls operator() for matching double type std::visit(Twice(), var3); // calls operator() for matching std::string type std::cout << std::get<int>(var1) << std::endl; std::cout << std::get<double>(var2) << std::endl; std::cout << std::get<std::string>(var3) << std::endl; return 0; }
结果如下:
注意,对象操作符应该为const函数,因为它们是无状态的(它们不改变它们的行为,只改变传递的值,即不改变成员变量的值)。
2. 使用泛型Lambdas访问
使用这个特性最简单的方法是使用泛型lambda,它是一个函数对象,用于任意类型:
例3:
#include <iostream> #include <variant> #include <string> auto printvariant = [](const auto& val) { std::cout << val << std::endl; }; int main() { std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit"); std::visit(printvariant, var1); std::visit(printvariant, var2); std::visit(printvariant, var3); return 0; }
结果如下:
这里,泛型lambda定义了一个闭包类型,其中函数调用操作符作为成员模板:
class CompilerSpecifyClosureTypeName { public: template<typename T> auto operator() (const T& val) const { std::cout << val << '\n'; } };
也可以使用lambda来修改当前选项的值:
例4:
#include <iostream> #include <variant> #include <string> auto printvariant = [](const auto& val) { std::cout << val << std::endl; }; int main() { std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit"); std::visit([](auto& val) { val = val + val; }, var1); std::visit([](auto& val) { val = val + val; }, var2); std::visit([](auto& val) { val = val + val; }, var3); std::visit(printvariant, var1); std::visit(printvariant, var2); std::visit(printvariant, var3); return 0; }
结果如下:
甚至可以使用编译时if语言特性以不同的方式处理不同的备选值:
例5:
#include <iostream> #include <variant> #include <string> auto dblvar = [](auto& val) { if constexpr (std::is_convertible_v<decltype(val), std::string>) { val = val + " test"; } else { val += 2; } }; int main() { std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit"); std::visit(dblvar, var1); std::visit(dblvar, var2); std::visit(dblvar, var3); std::cout << std::get<int>(var1) << std::endl; std::cout << std::get<double>(var2) << std::endl; std::cout << std::get<std::string>(var3) << std::endl; return 0; }
这里,对于一个std::string类型备选项,泛型lambda的调用实例化它的泛型函数调用模板来计算:
val = val + “ test”;
而对于其他类型备选项,如int或double, lambda的调用实例化其通用函数调用模板来计算:
val += 2;
结果如下:
3. 使用重载的Lambdas来访问
通过为函数对象和lambdas使用一个重载器,还可以定义一组lambdas,其中使用最佳匹配作为访问者。假设,重载器定义为重载,如下所示:
template<typename... Ts> struct overload : Ts... { using Ts::operator()...; }; // base types are deduced from passed arguments: template<typename... Ts> overload(Ts...) -> overload<Ts...>;
可以使用重载访问一个变量,为每个选项提供lambdas:
std::variant<int, std::string> var(42); ... std::visit(overload{ // calls best matching lambda for current alternative [](int i) { std::cout << "int: " << i << '\n'; }, [](const std::string& s) { std::cout << "string: " << s << '\n'; }, }, var);
还可以使用泛型lambda。总是用最好的搭配。例如,要修改variant对象的当前类型备选项的值,可以使用重载将字符串和其他类型的值“加倍”:
auto twice = overload{ [](std::string& s) { s += s; }, [](auto& i) { i *= 2; }, };
使用此重载,对于字符串类型备选项,将添加当前值,而对于所有其他类型,将值乘以2,这演示了variant对象的以下应用程序:
std::variant<int, std::string> var(42); std::visit(twice, var); // value 42 becomes 84 ... var = "hi"; std::visit(twice, var); // value "hi" becomes "hihi"
例 6:
#include <iostream> #include <variant> #include <string> template<typename... Ts> struct overload : Ts... { using Ts::operator()...; }; template<typename... Ts> overload(Ts...)->overload<Ts...>; auto twice = overload{ [](std::string& s) { s += s; }, [](auto& i) { i *= 2; }, }; int main() { std::variant<int, std::string> var1(42) , var3("visit"); std::visit(twice, var1); std::visit(twice, var3); std::visit(overload{ // calls best matching lambda for current alternative [](int i) { std::cout << "int: " << i << '\n'; }, [](const std::string& s) { std::cout << "string: " << s << '\n'; }, }, var1); std::visit(overload{ // calls best matching lambda for current alternative [](int i) { std::cout << "int: " << i << '\n'; }, [](const std::string& s) { std::cout << "string: " << s << '\n'; }, }, var3); return 0; }
结果如下:
到此这篇关于C++17之std::visit的具体使用的文章就介绍到这了,更多相关C++17 std::visit内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!