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

C++20中如何结合std::variant与指针处理对象生命周期?

用std::variant实现2D形状与射线相交检测的C++20方案

针对你想用std::variant替代继承实现运行时多态的需求,这里提供一个符合C++ idiom的方案,解决指针转换和生命周期问题:

核心思路

放弃指针变体的冗余维护,采用值语义的std::variant存储形状,通过std::visit自动完成类型分发,配合重载的相交检测函数实现多态逻辑。同时用std::variant存储相交结果,保留具体类型的引用信息。

代码实现

1. 定义基础类型

#include <variant>
#include <cmath>
#include <cstdio>
#include <limits>
#include <type_traits>

// 射线类型
struct Ray {
    float origin_x, origin_y;
    float dir_x, dir_y;
};

// 具体形状类型
struct Circle {
    float x, y;
    float radius;
};

struct Square {
    float x, y; // 中心点坐标
    float side_length;
};

// 形状变体:用值语义存储,避免指针生命周期问题
using Shape = std::variant<Circle, Square>;

2. 定义相交结果类型

用std::variant存储不同形状的相交结果,直接引用原形状对象(避免拷贝):

struct CircleIntersection {
    const Circle& circle;
    float distance;
    float hit_x, hit_y;
};

struct SquareIntersection {
    const Square& square;
    float distance;
    float hit_x, hit_y;
};

// 相交结果:std::monostate表示无相交
using Intersection = std::variant<std::monostate, CircleIntersection, SquareIntersection>;

3. 实现重载的相交检测函数

为每种形状与射线的组合实现具体的相交逻辑:

Intersection intersect(const Ray& ray, const Circle& circle) {
    const float dx = circle.x - ray.origin_x;
    const float dy = circle.y - ray.origin_y;
    const float a = ray.dir_x * ray.dir_x + ray.dir_y * ray.dir_y;
    const float b = 2 * (dx * ray.dir_x + dy * ray.dir_y);
    const float c = dx*dx + dy*dy - circle.radius*circle.radius;
    const float discriminant = b*b - 4*a*c;

    if (discriminant < 0 || a == 0) {
        return std::monostate{};
    }

    const float sqrt_d = std::sqrt(discriminant);
    float t = (-b - sqrt_d) / (2*a);
    if (t < 0) {
        t = (-b + sqrt_d) / (2*a);
        if (t < 0) return std::monostate{};
    }

    return CircleIntersection{
        .circle = circle,
        .distance = t,
        .hit_x = ray.origin_x + t * ray.dir_x,
        .hit_y = ray.origin_y + t * ray.dir_y
    };
}

Intersection intersect(const Ray& ray, const Square& square) {
    const float half_side = square.side_length / 2;
    const float min_x = square.x - half_side;
    const float max_x = square.x + half_side;
    const float min_y = square.y - half_side;
    const float max_y = square.y + half_side;

    float t_near = -std::numeric_limits<float>::infinity();
    float t_far = std::numeric_limits<float>::infinity();

    if (ray.dir_x != 0) {
        const float t1 = (min_x - ray.origin_x) / ray.dir_x;
        const float t2 = (max_x - ray.origin_x) / ray.dir_x;
        t_near = std::max(t_near, std::min(t1, t2));
        t_far = std::min(t_far, std::max(t1, t2));
    }

    if (ray.dir_y != 0) {
        const float t1 = (min_y - ray.origin_y) / ray.dir_y;
        const float t2 = (max_y - ray.origin_y) / ray.dir_y;
        t_near = std::max(t_near, std::min(t1, t2));
        t_far = std::min(t_far, std::max(t1, t2));
    }

    if (t_far < 0 || t_near > t_far) {
        return std::monostate{};
    }

    const float t = (t_near >= 0) ? t_near : t_far;
    return SquareIntersection{
        .square = square,
        .distance = t,
        .hit_x = ray.origin_x + t * ray.dir_x,
        .hit_y = ray.origin_y + t * ray.dir_y
    };
}

4. 实现变体类型的相交分发

用std::visit自动匹配对应的相交函数:

Intersection intersect(const Ray& ray, const Shape& shape) {
    return std::visit([&ray](const auto& concrete_shape) {
        return intersect(ray, concrete_shape);
    }, shape);
}

5. 使用示例

int main() {
    const Ray ray{0.0f, 0.0f, 1.0f, 0.0f}; // 沿X轴正方向的射线
    const Shape shapes[] = {
        Circle{5.0f, 0.0f, 1.0f},
        Square{10.0f, 0.0f, 2.0f},
        Circle{1.0f, 3.0f, 0.5f} // 无相交的圆
    };

    for (const auto& shape : shapes) {
        const auto intersection = intersect(ray, shape);
        std::visit([](const auto& isect) {
            using T = std::decay_t<decltype(isect)>;
            if constexpr (std::is_same_v<T, std::monostate>) {
                printf("No intersection\n");
            } else if constexpr (std::is_same_v<T, CircleIntersection>) {
                printf("Intersected Circle at (%.2f, %.2f), distance %.2f\n",
                       isect.hit_x, isect.hit_y, isect.distance);
            } else if constexpr (std::is_same_v<T, SquareIntersection>) {
                printf("Intersected Square at (%.2f, %.2f), distance %.2f\n",
                       isect.hit_x, isect.hit_y, isect.distance);
            }
        }, intersection);
    }

    return 0;
}

方案优势

  • 无冗余类型维护:不需要同时维护值变体和指针变体,用单一的Shape变体即可。
  • 避免生命周期问题:值语义的变体存储形状,只要Shape实例的生命周期长于Intersection,引用就安全。如果需要动态创建形状,可改用std::variant<std::unique_ptr<Circle>, std::unique_ptr<Square>>,同样通过std::visit解引用处理。
  • 符合C++ idiom:利用std::visit和重载函数实现类型分发,替代虚函数的多态逻辑,编译期检查类型匹配,运行时开销低。
  • 保留类型信息:相交结果用std::variant存储,可直接访问具体形状的属性,无需向下转型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 20:48:11