You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在工厂模式中实现遍历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

火山引擎 最新活动