如何用可变参数模板泛化Parser类的ParseMany函数
泛化ParseMany以支持任意数量解析类型(C++20)
可以利用C++20的可变模板参数、lambda模板和折叠表达式,直接写出支持任意数量类型的泛化ParseMany函数,完全替代现有的多个重载版本。以下是改造后的完整代码:
#include <optional> #include <tuple> #include <utility> // 假设Lexer类已定义 class Lexer {}; class Parser { Lexer& lexer; public: template<typename ReturnType> std::optional<ReturnType> Parse(); Parser(Lexer& lexer) : lexer(lexer) {} // 泛化版本:支持任意数量的待解析类型参数 template<typename ReturnType, typename... Ts> std::optional<ReturnType> ParseMany() { // 存储所有解析成功的值 std::tuple<std::decay_t<Ts>...> parsed_values; bool parse_success = true; // 借助索引序列逐个解析每个类型 [&]<std::size_t... Is>(std::index_sequence<Is...>) { // 折叠表达式展开解析逻辑,按顺序执行 ( [&] { auto parsed_opt = Parse<std::tuple_element_t<Is, decltype(parsed_values)>>(); if (!parsed_opt) { parse_success = false; return; } // 转移值到tuple中,避免拷贝 std::get<Is>(parsed_values) = std::move(*parsed_opt); }(), ... ); }(std::index_sequence_for<Ts...>{}); if (!parse_success) { return std::nullopt; } // 将tuple中的值展开构造ReturnType并返回 return std::apply([](auto&&... args) { return ReturnType{ std::forward<decltype(args)>(args)... }; }, std::move(parsed_values)); } };
关键特性说明
- 可变模板参数:
typename... Ts接受任意数量的待解析类型,无需手动编写N个重载。 - 顺序解析与失败快速返回:严格按照模板参数顺序依次解析,只要有一个类型解析失败,立即标记失败并终止后续解析,和原重载逻辑完全一致。
- 高效值传递:使用
std::move转移解析结果,结合std::forward确保构造ReturnType时的完美转发,避免不必要的拷贝开销。 - C++20特性依赖:利用带模板参数的lambda(
<std::size_t... Is>)和std::index_sequence实现类型参数的遍历展开,VS2022完全支持这些特性。
使用示例
调用方式和原重载版本完全兼容,比如:
// 解析两个类型并构造MyStruct std::optional<MyStruct> result = parser.ParseMany<MyStruct, IntToken, StringToken>();
内容的提问来源于stack exchange,提问作者girobuz
相关产品推荐
相关产品推荐

