C++ 中的函数继承和 return 类型

Function inheritance and return types in C++

我正在学习 C++,我遇到了一个问题,我可以用我以前的编程经验(主要是 C 和 Java;一些但有限的 OOP 经验)来解决,但我想知道什么将是一个合适的、现代的 C++ 解决方案。问题涉及具有不同 return 类型的虚函数的继承和派生 classes 版本。基于多个 Stack Overflow 线程,这样的事情是不可能的。那么接下来我该如何处理呢?

为了练习 C++ 功能,我正在编写光线追踪器。我有一个虚拟基础 class Object 和派生 classes PolyhedronPolygon 来描述光可以与之交互的对象 Ray。 (实际上,我有中间虚拟 classes SolidFace, 以及派生的 classes SphereCylinderCircle PolyhedronPolygon,但为了简单起见,让我们在这里忘记它们。)目前,我只实现了光的发射和吸收,即 Ray 只直行,没有任何折射或反射。 Polyhedron 内的吸收与强度成正比(指数衰减),所以我必须计算出 Ray 通过的物体并将 Ray 的强度从其来源向前整合到哪里它击中了探测器。我有一个向量 std::vector<std::shared_ptr<Intersection>> intersections 来存储 Ray 与模拟场景中的对象的所有这些交集。交集需要包含交集 Points、相交的 Polygon 面和 Polyhedron 本身用于 Polyhedron 对象,或者交集 PointPolygon 面向自身 Polygon 对象。因此,我想派生 classes Intersection_PolyhedronIntersection_Polygon 来覆盖对 Intersection::modulate_intensity(const double intensity_before) const 的调用,它应该 return a Ray 通过相关物体后的强度。换句话说,我想避免检查相交对象的类型,而是在计算对 Ray 强度的调制时利用继承。

我想让每个 Ray 简单地循环遍历包含模拟场景中所有对象的向量 std::vector<std::shared_ptr<Object>> objects,调用虚函数 Object::get_intersection(const Ray& ray) const 并获得 Intersection_Polyhedron 或 return 中的 Intersection_Polygon 基于交集的类型(如果它是 PolyhedronPolygon)。指向这些派生交叉点对象的指针将被推回 intersectionsintersections 将根据与 Ray 原点的距离进行排序,然后循环调用并覆盖 Intersection::modulate_intensity() 来确定 Ray 在检测器上的最终强度。对我来说,这听起来像是实现此目的的 C++/OOP 方式,但这似乎不可能,因为派生的 classes 版本的基 class 的虚函数必须具有相同的return 类型。那我应该怎么做呢?

(目前,我 return 来自 get_intersection()Intersection 的单数类型,用于 PolyhedronsPolygons。作为其成员,一个 Intersection 具有用于相交 Points 和相交 std::shared_ptr<Polygon> 面的矢量,以及一个 std::shared_ptr<Polyhedron> (对于 Polygon 是一个 nullptr,因为没有体积)。为了区分 PolyhedronsPolygons 的交集,我简单地检查是否有一个或两个交集 Points。这不是太不优雅,但现代 C++ 必须提供更好的方法通过继承实现这一点,对吗?)

一些非常类似于 C++ 的伪代码,以进一步阐明我想要实现的目标:

// ...

// create objects in a scene
std::vector<std::shared_ptr<Object>> objects;
// ...

// find a ray's intersections with the objects
std::vector<std::shared_ptr<Intersection>> intersections;
for(const auto& object : objects) {
  // virtual class Object's function overridden with that of Polyhedron or Polygon
  // returns std::shared_ptr<Intersection_Polyhedron> or std::shared_ptr<Intersection_Polygon> based on type of object
  auto intersection = object->get_intersection(ray);
  intersections.push_back(intersection);
}

// sort the intersections with std::sort and a lambda expression
// ...

// calculate a ray's intensity
double intensity = 0.0;
for(const auto& intersection : intersections) {
  // virtual class Intersection's function overridden with that of Intensity_Polyhedron or Intensity_Polygon
  intensity = intersection->modulate_intensity(intensity);
}

// ...

Returning 界面总体上没问题:

class Ray;

struct Intersection
{
    virtual ~Intersection() = default;
    virtual double modulate_intensity(double intensity) = 0;
};
struct Intersection_Polygon : Intersection
{
    double modulate_intensity(double intensity) override {/**/}
};
struct Intersection_Polyhedron : Intersection
{
    double modulate_intensity(double intensity) override {/**/}
};

struct Object
{
    virtual ~Object() = default;
    virtual std::shared_ptr<Intersection> get_intersection(const Ray&) = 0;
};

struct Polygon : Object
{
    std::shared_ptr<Intersection> get_intersection(const Ray&) override {
        return std::make_shared<Intersection_Polygon>();
    }
};
struct Polyhedron : Object
{
    std::shared_ptr<Intersection> get_intersection(const Ray&) override {
        return std::make_shared<Intersection_Polyhedron>();
    }
};

Return 类型可以通过协变来改进,但 C++ 只处理引用和 (non-smart) 指针。所以它需要一些样板来为智能指针模拟它:

struct Object
{
    virtual ~Object() = default;
    std::shared_ptr<Intersection> get_intersection(const Ray& ray)
    {
        return std::shared_ptr<Intersection>{get_intersection_ptr(ray)};
    }
protected:
    virtual Intersection* get_intersection_ptr(const Ray&) = 0;
};

struct Polygon : Object
{
    std::shared_ptr<Intersection_Polygon> get_intersection(const Ray& ray)
    {
        return std::shared_ptr<Intersection_Polygon>{get_intersection_ptr(ray)};
    }
protected:
    Intersection_Polygon* get_intersection_ptr(const Ray&) override {
        return new Intersection_Polygon();
    }
};

模板(可能是 CRTP)可能有助于分解样板文件:

template <typename IntersectionType>
struct ObjectT : Object
{
    std::shared_ptr<IntersectionType> get_intersection(const Ray& ray)
    {
        return std::shared_ptr<IntersectionType>{get_intersection_ptr(ray)};
    }
protected:
    IntersectionType* get_intersection_ptr(const Ray&) override {
        return new IntersectionType();
    }
};

struct Polygon : ObjectT<Intersection_Polygon> {};
struct Polyhedron : ObjectT<Intersection_Polyhedron> {};