C++模板参数包位置错误修复:高性能Pipeline类实现方案
修复Pipeline类模板参数错误的高性能方案
错误原因
你的代码编译报错有两个核心原因:
- C++标准规定,一个模板只能包含一个可变参数包,且必须放在模板参数列表的最后位置。原代码同时使用了
typename... Args和StageFunctionRefType<...>... StageFunctions两个参数包,违反了这一规则。 - 即使只保留一个参数包,
Args位于StageFunctions之前的写法也不符合参数包必须后置的要求。
修复方案(C++17+)
使用C++17引入的**auto模板参数**直接传递阶段函数指针,既能避免参数包冲突,又能让编译器自动推导函数签名,全程保持编译期解析,完全避免虚函数或std::function带来的运行时开销。
修复后完整代码
#include <array> #include <tuple> #include <type_traits> #include <utility> enum class ParseState { EXTRACTED, ///< 阶段处理完成,已提取有效数据 IN_PROGRESS, ///< 阶段处理中,需要更多输入数据 REJECTED, ///< 数据无效,处理终止 }; struct ParseStage { std::size_t stage = 0; std::size_t get_stage() const noexcept { return stage; } std::size_t next_stage() noexcept { return ++stage; } void reset() noexcept { stage = 0; } }; template <typename InputDataType> using ParseResult = std::pair<ParseState, InputDataType>; // 核心Pipeline类:使用auto模板参数接收阶段函数指针 template <typename InputDataType, typename ParserState, auto... StageFunctions> class Pipeline { // 存储阶段函数的编译期数组(constexpr保证编译期初始化) static constexpr std::array Stages{ StageFunctions... }; // 元工具:从阶段函数签名中提取额外参数类型 template <typename Fn> struct ExtractArgs; // 处理函数引用的情况 template <typename... Args> struct ExtractArgs<ParseResult<InputDataType>(const InputDataType&, ParserState&, Args...)> { using ArgsTuple = std::tuple<Args...>; }; // 处理函数指针的情况 template <typename... Args> struct ExtractArgs<ParseResult<InputDataType>(*)(const InputDataType&, ParserState&, Args...)> { using ArgsTuple = std::tuple<Args...>; }; // 所有阶段函数必须共享相同的额外参数签名 using ExpectedArgs = typename ExtractArgs<decltype(Stages[0])>::ArgsTuple; public: template <typename... Args> static ParseResult<InputDataType> feed(const InputDataType& input_data, ParserState& parser_state, ParseStage& parse_stage, Args&&... args) { // 编译期检查:传入的额外参数必须与阶段函数签名匹配 static_assert(std::is_same_v<std::tuple<std::decay_t<Args>...>, ExpectedArgs>, "额外参数类型与阶段函数签名不匹配"); // 已被拒绝的状态直接返回 if (parser_state.parse_state == ParseState::REJECTED) return { ParseState::REJECTED, input_data }; auto remaining_data = input_data; while (parse_stage.get_stage() < Stages.size() && !remaining_data.empty()) { const auto parse_result = Stages[parse_stage.get_stage()](remaining_data, parser_state, std::forward<Args>(args)...); remaining_data = parse_result.second; switch (parse_result.first) { case ParseState::EXTRACTED: parse_stage.next_stage(); break; case ParseState::REJECTED: return parse_result; case ParseState::IN_PROGRESS: // 需要更多数据,退出循环 goto exit_loop; } } exit_loop: // 所有阶段处理完成 if (parse_stage.get_stage() == Stages.size()) return { ParseState::EXTRACTED, remaining_data }; return { ParseState::IN_PROGRESS, remaining_data }; } };
使用示例
假设你有如下阶段函数和解析状态类:
// 自定义解析状态类 struct MyParserState { ParseState parse_state = ParseState::IN_PROGRESS; // 其他解析状态数据... }; // 阶段函数1:接收std::string输入,额外参数为int ParseResult<std::string> stage1(const std::string& data, MyParserState& state, int threshold) { // 实现处理逻辑... if (data.size() >= threshold) return { ParseState::EXTRACTED, data.substr(threshold) }; return { ParseState::IN_PROGRESS, data }; } // 阶段函数2:签名与stage1一致 ParseResult<std::string> stage2(const std::string& data, MyParserState& state, int threshold) { // 实现处理逻辑... if (data.find("end") != std::string::npos) return { ParseState::EXTRACTED, data.substr(data.find("end") + 3) }; return { ParseState::IN_PROGRESS, data }; } // 定义Pipeline类型 using MyStringPipeline = Pipeline<std::string, MyParserState, &stage1, &stage2>; // 使用Pipeline int main() { MyParserState state; ParseStage stage; std::string input = "hello123endxyz"; auto result = MyStringPipeline::feed(input, state, stage, 5); // 处理result... return 0; }
方案优势
- 极致性能:所有阶段函数调用都是编译期确定的直接函数调用,无虚函数表查询或
std::function的类型擦除开销。 - 类型安全:编译期检查阶段函数签名一致性,以及传入参数的匹配性,提前发现错误。
- 简洁易用:使用
auto模板参数直接传递函数指针,无需手动指定复杂的函数类型。
内容的提问来源于stack exchange,提问作者Amir Hossein Sarebani
相关产品推荐
相关产品推荐

