探索C++17的variant类型及其应用
variant类型解析
variant是C++17标准库引入的一种多值容器类型。variant<A, B, C>可以存储A、B或C类型的数据。
- 与传统C语言union类似,variant也是一种联合类型,能够容纳多种数据类型,但在任意时刻只能保存其中一种类型的数据。
- 与传统union不同的是,variant是一种类型安全的联合类型,能够自行识别当前存储的数据类型,无需外部辅助。
例如,var = variant<int, float, std::string>表示var是一个可以存储整数、浮点数或字符串的对象。
var.index()返回当前存储数据类型的索引位置,第一个类型索引为0,第二个为1,依此类推。std::holds_alternative<T>(var)检查variant是否存储了类型T的数据。std::get<I>(var)当variant存储的数据类型索引为I时返回该数据,否则抛出异常。std::get_if<I>(&var)当variant存储的数据类型索引为I时返回指向该数据的指针,否则返回nullptr。std::get<T>(var)当variant存储的数据类型为T时返回该数据,否则抛出异常。std::get_if<T>(&var)当variant存储的数据类型为T时返回指向该数据的指针,否则返回nullptr。
#include <iostream>
#include <string>
#include <variant>
int main()
{
variant<int, float, std::string> data; // data默认值为0
data = 42;
bool contains_int = holds_alternative<int>(data);
bool contains_float = holds_alternative<float>(data);
std::cout << data.index() << contains_int << contains_float
<< get<0>(data) << *get_if<0>(&data) << std::endl; // 10142
data = 3.14f;
std::cout << data.index() << (get_if<int>(&data) == nullptr)
<< get<1>(data) << get<float>(data) << std::endl; // 113.143.14
data = "Hello";
std::cout << data.index() << get<2>(data) << get<std::string>(data) << std::endl; // 2HelloHello
}
std::visit函数
std::visit(visitor, var)将variant中存储的数据作为参数传递给访问者函数visitor。
对于多个variant,std::visit(visitor, var1, var2, ...)会将所有variant中存储的数据作为参数传递给访问者函数。
#include <iostream>
#include <string>
#include <variant>
#include <boost/hana/functional/overload.hpp>
struct DataVisitor {
void operator()(int number) const {
std::cout << "整数: " << number << std::endl;
}
void operator()(double decimal) const {
std::cout << "浮点数: " << decimal << std::endl;
}
void operator()(const std::string& text) const {
std::cout << "字符串: " << text << std::endl;
}
};
template<typename... Funcs> struct MultiVisitor : Funcs... {
using Funcs::operator()...;
};
template<typename... Funcs> MultiVisitor(Funcs...) -> MultiVisitor<Funcs...>;
int main()
{
variant<int, double, std::string> container;
// 使用lambda作为访问者
auto lambda_visitor = [](const auto& value) {
std::cout << value << std::endl;
};
// 使用函数对象作为访问者
DataVisitor function_visitor;
// 使用重载lambda作为访问者
MultiVisitor overloaded_visitor{
[](int num){std::cout << "整数: " << num << std::endl;},
[](double dbl){std::cout << "浮点数: " << dbl << std::endl;},
[](const std::string& str){std::cout << "字符串: " << str << std::endl;}
};
// 使用boost::hana::overload创建访问者
auto hana_visitor = boost::hana::overload(
[](int num){std::cout << "整数: " << num << std::endl;},
[](double dbl){std::cout << "浮点数: " << dbl << std::endl;},
[](const std::string& str){std::cout << "字符串: " << str << std::endl;}
);
// 使用编译期if的lambda访问者
auto conditional_visitor = [](const auto& arg) {
using ValueType = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<ValueType, int>) {
std::cout << "整数: " << arg << std::endl;
}
else if constexpr (std::is_same_v<ValueType, double>) {
std::cout << "浮点数: " << arg << std::endl;
}
else if constexpr (std::is_same_v<ValueType, std::string>) {
std::cout << "字符串: " << arg << std::endl;
}
};
container = 100;
std::visit(lambda_visitor, container);
std::visit(function_visitor, container);
std::visit(overloaded_visitor, container);
std::visit(hana_visitor, container);
std::visit(conditional_visitor, container);
container = 3.14;
std::visit(lambda_visitor, container);
std::visit(function_visitor, container);
std::visit(overloaded_visitor, container);
std::visit(hana_visitor, container);
std::visit(conditional_visitor, container);
container = "World";
std::visit(lambda_visitor, container);
std::visit(function_visitor, container);
std::visit(overloaded_visitor, container);
std::visit(hana_visitor, container);
std::visit(conditional_visitor, container);
}
- lambda_visitor和conditional_visitor是泛型lambda,可以接受任何类型参数。
- lambda_visitor不区分参数类型。
- conditional_visitor使用编译期if语句来区分参数类型。
- function_visitor和overloaded_visitor是函数对象,通过重载函数调用操作符来区分参数类型。
- function_visitor的函数调用操作符在自身中定义。
- overloaded_visitor的函数调用操作符继承自三个lambda。
- hana_visitor通过boost::hana::overload函数生成,能够从多个lambda中选择合适的处理指定参数。
- 代码示例中使用了三个C++17新特性:
- 类模板的自动推导指引
- 变长using声明
- 编译期if语句
