在工厂模式中实现遍历std::tuple的递归函数
递归遍历std::tuple的ConsoleShapeFactory实现
嘿,我来帮你搞定这个递归遍历std::tuple的问题!首先,我们需要给你的ConsoleShapeFactory补上递归逻辑,核心是用索引递推的方式逐个处理tuple里的每个形状,同时配合编译期类型判断来调用对应的创建/初始化函数。
完整代码实现
先给你补全后的完整代码,里面包含了递归遍历的核心逻辑,还有扩展的形状类型示例:
#include <tuple> #include <memory> #include <iostream> #include <type_traits> // 先定义基础的Shape类和CAD命名空间下的形状(假设你已经有这些,这里补个示例) class Shape {}; namespace CAD { class Point : public Shape { public: double x, y; Point() : x(0.0), y(0.0) {} }; class Line : public Shape { public: std::shared_ptr<Point> start, end; Line() : start(std::make_shared<Point>()), end(std::make_shared<Point>()) {} }; } class ShapeFactory { public: virtual ~ShapeFactory() = default; }; template<typename... Shapes> class ConsoleShapeFactory : public ShapeFactory { private: // 你已经定义的Point创建/初始化函数 void MakePoint(std::shared_ptr<CAD::Point> point) { std::cout << "输入Point的X坐标: "; std::cin >> point->x; std::cout << "输入Point的Y坐标: "; std::cin >> point->y; } // 扩展一个Line的初始化函数(示例用) void MakeLine(std::shared_ptr<CAD::Line> line) { std::cout << "\n设置Line的起点:\n"; MakePoint(line->start); std::cout << "\n设置Line的终点:\n"; MakePoint(line->end); } // 递归终止条件:当索引等于tuple长度时,结束递归 template<size_t Index = 0> std::enable_if_t<Index >= std::tuple_size_v<std::tuple<Shapes...>>> MakeShapeRouter(std::tuple<Shapes...>&) {} // 递归处理逻辑:处理当前索引的元素,然后递归处理下一个 template<size_t Index = 0> std::enable_if_t<Index < std::tuple_size_v<std::tuple<Shapes...>>> MakeShapeRouter(std::tuple<Shapes...>& shapes_tuple) { // 取出当前索引的形状实例 auto current_shape = std::get<Index>(shapes_tuple); // 编译期判断类型,调用对应的初始化函数 using ShapeType = typename std::remove_pointer_t< typename std::remove_reference_t<decltype(current_shape)> >; if constexpr (std::is_same_v<ShapeType, CAD::Point>) { MakePoint(current_shape); } else if constexpr (std::is_same_v<ShapeType, CAD::Line>) { MakeLine(current_shape); } // 你可以在这里加更多形状的判断分支 // 递归处理下一个索引的元素 MakeShapeRouter<Index + 1>(shapes_tuple); } public: // 对外提供的创建函数:生成并初始化整个形状tuple std::tuple<Shapes...> CreateShapes() { std::tuple<Shapes...> shapes_tuple; // 启动递归遍历初始化 MakeShapeRouter(shapes_tuple); return shapes_tuple; } };
关键逻辑解释
- 递归的终止与递推:
我们用模板参数Index标记当前处理的tuple元素位置,当Index等于tuple长度时(通过std::tuple_size_v获取),触发终止版本的MakeShapeRouter结束递归;否则处理当前元素,再递归调用Index+1的版本处理下一个元素。 - 编译期类型判断:
用constexpr if在编译期判断当前形状的类型,调用对应的MakeXXX函数,完全没有运行时类型检查的开销,效率拉满。 - 扩展灵活性:
要是你要支持新形状(比如Circle),只需要新增一个MakeCircle函数,再在constexpr if里加个类型判断分支就行,非常方便。
使用示例
int main() { // 创建工厂,指定要生成的形状tuple类型:Point和Line的智能指针 ConsoleShapeFactory<std::shared_ptr<CAD::Point>, std::shared_ptr<CAD::Line>> factory; auto my_shapes = factory.CreateShapes(); // 输出验证结果 auto point = std::get<0>(my_shapes); std::cout << "\n创建的Point: (" << point->x << ", " << point->y << ")\n"; auto line = std::get<1>(my_shapes); std::cout << "创建的Line: 从(" << line->start->x << ", " << line->start->y << ")到(" << line->end->x << ", " << line->end->y << ")\n"; return 0; }
优化小技巧
如果你的形状类型很多,写一堆if constexpr分支会显得臃肿,你可以用模板特化来简化:
// 定义一个通用的MakeShape模板 template<typename T> void MakeShape(std::shared_ptr<T> shape); // 特化Point版本 template<> void MakeShape<CAD::Point>(std::shared_ptr<CAD::Point> shape) { MakePoint(shape); } // 特化Line版本 template<> void MakeShape<CAD::Line>(std::shared_ptr<CAD::Line> shape) { MakeLine(shape); }
然后在递归函数里只需要一行代码:MakeShape(current_shape);,不用再写一堆判断分支,代码会清爽很多!
内容的提问来源于stack exchange,提问作者EliSquared




