C++14下如何优雅重构含variadic function的条件分支代码以简化save函数调用
C++14下如何优雅重构含variadic function的条件分支代码以简化save函数调用
我完全理解你的痛点——随着新增可选类型的增多,嵌套的if-else分支会越来越臃肿,可读性直线下降。你想要只用一次save调用,自动把所有选中的类型传进去,同时又不能修改原有结构体和save函数,还要兼容C++14,这个需求非常合理。
你提到的std::tuple + std::tuple_cat思路其实非常可行,我们可以把这个思路包装得更优雅,彻底消除嵌套判断,同时保持逻辑清晰。核心思路是:把每个可选类型的条件创建、预处理(processFoo调用)、是否加入参数列表的逻辑封装起来,通过空tuple或单元素tuple来标记是否包含该类型,最后把所有需要的tuple拼接成一个完整的tuple,再展开传给save。
具体实现方案
1. 实现辅助工具函数
首先我们需要两个辅助工具:一个是处理单个可选类型的maybe_add函数,另一个是把tuple展开为可变参数的apply函数(因为C++14还没有标准库的std::apply):
#include <iostream> #include <tuple> #include <utility> // 用于std::index_sequence // 原有的不可修改的结构体和函数保持不变 struct Foo1 {int f;}; struct Foo2 {int g;}; struct Foo3 {int h;}; void processFoo(Foo1 &foo) { } void processFoo(Foo2 &foo) { } void processFoo(Foo3 &foo) { } bool selectFoo2() { return true; } bool selectFoo3() { return false; } template<typename ...FooT> void save(const FooT &...foos) { std::cout << "foos = " << sizeof...(foos) << std::endl; } // 辅助函数:根据条件决定是否创建对象、处理,并返回对应的tuple(含对象或空) template <typename T, typename CondFunc> auto maybe_add(CondFunc cond) { if (cond()) { T obj; processFoo(obj); return std::make_tuple(obj); // 选中则返回含该对象的tuple } else { return std::tuple<>{}; // 未选中则返回空tuple } } // 辅助函数:将tuple展开为可变参数传给目标函数(模拟C++17的std::apply) namespace detail { template <typename Func, typename Tuple, size_t... I> decltype(auto) apply_impl(Func&& func, Tuple&& t, std::index_sequence<I...>) { return std::forward<Func>(func)(std::get<I>(std::forward<Tuple>(t))...); } } // namespace detail template <typename Func, typename Tuple> decltype(auto) apply(Func&& func, Tuple&& t) { return detail::apply_impl( std::forward<Func>(func), std::forward<Tuple>(t), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{} ); }
2. 重构handle_foos函数
有了上面的辅助工具,handle_foos可以简化到极致,完全消除嵌套分支,新增类型只需要加一行代码:
void handle_foos() { // 处理必选的Foo1 Foo1 f1; processFoo(f1); auto base_tuple = std::make_tuple(f1); // 处理每个可选类型,自动生成对应的tuple(空或含对象) auto foo2_tuple = maybe_add<Foo2>(selectFoo2); auto foo3_tuple = maybe_add<Foo3>(selectFoo3); // 拼接所有需要的tuple,得到包含所有选中对象的tuple auto all_selected_foos = std::tuple_cat(base_tuple, foo2_tuple, foo3_tuple); // 把tuple展开,一次性调用save apply(save<>, all_selected_foos); } int main(int, char**) { handle_foos(); return 0; }
方案优势
- 可读性极强:所有可选类型的逻辑独立,新增类型只需添加一行
maybe_add调用,无需修改复杂的嵌套分支 - 完全兼容需求:没有修改原有的结构体、
processFoo和save函数,严格符合C++14标准 - 行为完全一致:和原代码的对象创建、
processFoo调用时机、save参数列表完全匹配 - 扩展性好:后续新增
Foo4、Foo5等类型,只需要添加auto foo4_tuple = maybe_add<Foo4>(selectFoo4);并把它加入std::tuple_cat的参数即可
额外说明
- 如果你的结构体不可拷贝,只需把
maybe_add里的return std::make_tuple(obj);改成return std::make_tuple(std::move(obj));,利用移动语义避免拷贝 - 如果条件判断需要参数,直接用lambda包装即可,比如
maybe_add<Foo2>([](){ return some_condition(123, "abc"); })
内容来源于stack exchange
相关产品推荐
相关产品推荐

