在 C++17 之后,std::variant 与 std::optional 成为标准库中非常实用的类型,用来替代传统的裸指针或手动管理内存的方式。本文将通过一个具体的业务场景——实现一个简单的表单数据校验器,演示如何利用这两个类型提升代码安全性、可读性和维护性。
1. 背景:表单数据的多态结构
假设我们正在开发一个多租户系统,每个租户的表单字段类型各不相同:
- 有的字段为字符串(如“用户名”)
- 有的字段为整数(如“年龄”)
- 有的字段可选(如“备注”)
- 有的字段是枚举(如“性别”)
传统做法常用 void* 或 boost::variant,需要显式检查类型并转换,容易出错。C++17 的 std::variant 可以直接把所有可能的类型打包在一个容器里,std::optional 则能自然表示“可选”字段。
2. 设计数据结构
#include <variant>
#include <optional>
#include <string>
#include <vector>
#include <unordered_map>
#include <iostream>
// 枚举类型
enum class Gender { Male, Female, Other };
// 表单字段的值类型
using FieldValue = std::variant<
std::string, // 文本
int, // 整数
double, // 浮点
bool, // 布尔
Gender, // 枚举
std::optional<std::string> // 可选文本
>;
// 表单字段描述
struct FieldDef {
std::string name;
bool required;
};
// 表单数据
using FormData = std::unordered_map<std::string, FieldValue>;
using FormDefs = std::vector <FieldDef>;
3. 解析与验证流程
3.1 解析
我们从 JSON(这里只用字符串演示)解析成 FormData。在解析过程中,根据字段名称判断应该存入哪种类型。
FieldValue parseField(const std::string& key, const std::string& value) {
if (key == "age") {
return std::stoi(value);
} else if (key == "salary") {
return std::stod(value);
} else if (key == "gender") {
if (value == "male") return Gender::Male;
if (value == "female") return Gender::Female;
return Gender::Other;
} else if (key == "active") {
return value == "true";
} else {
return value; // 默认文本
}
}
3.2 验证
使用 std::variant 的 `std::holds_alternative