You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C++20 Concepts替代抽象接口作为shared_ptr模板参数及构建容器

用C++20 Concepts实现可Hittable对象容器的方案

Concept是编译期约束,不能直接作为指针类型存储(这就是你遇到“不完整类型”错误的原因)。要在保留Concept约束的同时最小化运行时开销,有两种可行方案:


方案一:自定义轻量类型擦除(与原虚函数开销一致)

这种方案不需要让所有Hittable类型继承统一基类,而是通过包装类自动适配符合Concept的类型,保持虚函数级别的运行时开销。

步骤1:定义Hittable Concept

#include <concepts>
#include <memory>
#include <vector>

// 假设已有Ray和HitRecord定义
struct Ray {};
struct HitRecord { double t; /* 其他字段 */ };

template<typename T>
concept Hittable = requires(const T& a, const Ray& r, double t_min, double t_max, HitRecord& rec) {
    { a.hit(r, t_min, t_max, rec) } -> std::convertible_to<bool>;
};

步骤2:实现类型擦除包装类

// 底层抽象类(仅用于类型擦除,无需业务类继承)
class HittableErased {
public:
    virtual ~HittableErased() = default;
    virtual bool hit(const Ray& r, double t_min, double t_max, HitRecord& rec) const = 0;
};

// 模板实现类,自动适配所有符合Hittable Concept的类型
template<Hittable T>
class HittableErasedImpl : public HittableErased {
public:
    explicit HittableErasedImpl(std::shared_ptr<T> obj) : obj_(std::move(obj)) {}
    bool hit(const Ray& r, double t_min, double t_max, HitRecord& rec) const override {
        return obj_->hit(r, t_min, t_max, rec);
    }
private:
    std::shared_ptr<T> obj_;
};

// 对外暴露的包装类
class HittableWrapper {
public:
    template<Hittable T>
    explicit HittableWrapper(std::shared_ptr<T> obj) 
        : impl_(std::make_unique<HittableErasedImpl<T>>(std::move(obj))) {}

    bool hit(const Ray& r, double t_min, double t_max, HitRecord& rec) const {
        return impl_->hit(r, t_min, t_max, rec);
    }
private:
    std::unique_ptr<HittableErased> impl_;
};

步骤3:实现HittableList容器

class HittableList {
public:
    // 自动约束添加的对象必须符合Hittable Concept
    void add(std::shared_ptr<Hittable auto> obj) {
        objects.emplace_back(std::move(obj));
    }

    bool hit(const Ray& r, double t_min, double t_max, HitRecord& rec) const {
        HitRecord temp_rec;
        bool hit_anything = false;
        double closest_so_far = t_max;

        for (const auto& obj : objects) {
            if (obj.hit(r, t_min, closest_so_far, temp_rec)) {
                hit_anything = true;
                closest_so_far = temp_rec.t;
                rec = temp_rec;
            }
        }
        return hit_anything;
    }
private:
    std::vector<HittableWrapper> objects;
};

方案二:std::variant编译期分发(零额外运行时开销)

如果你的可Hittable类型数量固定(比如Sphere、Plane、Triangle等),可以用std::variant存储所有可能的类型,通过std::visit实现编译期分发,完全避免虚函数开销。

步骤1:定义变体类型与访问器

#include <variant>

// 假设已有具体的Hittable类型实现
struct Sphere {
    bool hit(const Ray& r, double t_min, double t_max, HitRecord& rec) const {
        // 球体相交逻辑
        return false;
    }
};

struct Plane {
    bool hit(const Ray& r, double t_min, double t_max, HitRecord& rec) const {
        // 平面相交逻辑
        return false;
    }
};

// 定义包含所有Hittable类型的变体
using HittableVariant = std::variant<std::shared_ptr<Sphere>, std::shared_ptr<Plane>>;

// 实现hit方法的访问器
struct HitVisitor {
    const Ray& r;
    double t_min;
    double& closest_so_far;
    HitRecord& rec;
    bool& hit_anything;

    template<Hittable T>
    void operator()(const std::shared_ptr<T>& obj) const {
        HitRecord temp_rec;
        if (obj->hit(r, t_min, closest_so_far, temp_rec)) {
            hit_anything = true;
            closest_so_far = temp_rec.t;
            rec = temp_rec;
        }
    }
};

步骤2:实现HittableList容器

class HittableList {
public:
    void add(HittableVariant obj) {
        objects.push_back(std::move(obj));
    }

    bool hit(const Ray& r, double t_min, double t_max, HitRecord& rec) const {
        bool hit_anything = false;
        double closest_so_far = t_max;
        HitVisitor visitor{r, t_min, closest_so_far, rec, hit_anything};

        for (const auto& var : objects) {
            std::visit(visitor, var);
        }
        return hit_anything;
    }
private:
    std::vector<HittableVariant> objects;
};

方案选择建议

  • 类型数量固定:优先选std::variant方案,零额外运行时开销,编译期分发性能最优。
  • 类型数量不确定:选自定义类型擦除方案,开销与原虚函数接口完全一致,但无需强制所有业务类继承统一基类,灵活性更高。

内容的提问来源于stack exchange,提问作者Soumen Pradhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 20:13:32