c++ 是否有等同于 Typescript 的交集类型?

Does c++ have an equivalent to Typescripts' intersection type?

我在使用 Typescript 一段时间后回到了 C++,了解到在 Typescript 中我们可以做这样的事情:

let ojbect: InterfaceA & InterfaceB = new InterfaceAandBImplementation();

它被称为 Intersection Type。我想在 C++ 中做同样的事情,使用纯虚拟 classes 作为接口,但我似乎无法完成同样的事情……这变得很烦人。显然,Java 在泛型中也支持它 - 据我所知 - 所以它不仅仅是 TS。

具体用例

我的 OpenGL 应用程序有 LightCamera classes,我想将它们的实例附加到世界对象。

class Camera
    : public Interface::Positionable
    , public Interface::Directionable
{ ... }

class Light
{ ... }

class SpotLight
    : public Light
    , public Interface::Positionable
    , public Interface::Directionable
{ ... }

这些是接口:

namespace Interface
{
    class Positionable
    {
    public:
        virtual const glm::vec3& getPosition() const = 0;

        virtual void addToPosition(const glm::vec3& position) = 0;

        virtual void setPosition(const glm::vec3& position) = 0;
    };

    class Directionable
    {
    public:
        virtual const glm::vec3& getDirection() const = 0;

        virtual void addToDirection(const glm::vec3& direction) = 0;

        virtual void setDirection(const glm::vec3& direction) = 0;
    };
}

使用 FixedAttachment class 我想用相机跟踪物体,但也用这个 class 附加聚光灯(例如宇宙飞船的头灯)。

class FixedAttachment
{
public:
    FixedAttachment(std::shared_ptr<Interface::Positionable> target, std::shared_ptr<Interface::Directionable> tracker, const glm::vec3& position)
        : Attachment{ target },
        m_tracker{ tracker },
        m_position{ position }
    {
    }

    std::shared_ptr<Interface::Positionable> getSharedTarget() const { return m_target; }
    Interface::Positionable* getTarget() const { return m_target.get(); }
    const glm::vec3& getPosition() const { return m_position; }

    void updateTracker()
    {
        /* Calculate new direction, position here for the tracker based upon target */
        // m_tracker->setDirection(calculatedDirection);
        // m_tracker->setPosition(calculatedPosition);
        // ^^^^^^^^^ impossible, not a positionable yet
    }

private:
    std::shared_ptr<Interface::Positionable> m_target;
    std::shared_ptr<Interface::Directionable> m_tracker;
    glm::vec3 m_position;
};

正如 updateTracker 中的代码注释所指出的,我需要 m_tracker 才能成为 Interface::Positionable

所以我真正想要的只是像这样存储我的 m_tracker

std::shared_ptr<Interface::Positionable & Interface::Directionable> m_tracker;

(并修改构造函数等)我可以通过创建一个结合其他 2:

的接口 class 来解决这个问题
class PosDirIntersection
    : public Interface::Positionable,
    : public Interface::Directionable
{}

但这只是避免了这个问题,因为那时我还必须从中派生相机和聚光灯才能通过它。如果我随后创建一个接受类型 LightDirIntersection (Light & Interface::Directionable) 变量的函数,我将无法再向它传递类型 SpotLight 的变量。另外,它迫使我修改 classes 只是为了将它传递到他们定义之外的某个地方,这就是我不喜欢这个解决方案的原因。

到目前为止,我还没有找到解决这个问题的办法。有吗?

在 typescript 中,type C = A & B 基本上是一种欺骗你获得 interface C extends A, B {} 的方法。

这样做只是简单地从左到右合并成员。如果这是您想要的,那么 class C : A, B {} 可能是最接近的。

否则,对于函数中的类型要求,您将需要概念(或 SFINAE 等价物,但很快就会变得混乱):

template <class T>
concept Positionable = requires(T obj){
  { std::is_base_of_v<Interface::Positionable, T> }
}


template <class T>
concept Directionable = requires(T obj){
  { std::is_base_of_v<Interface::Directionable, T> }
}

然后您可以对概念进行任意组合并在您的函数中要求它们:

void doStuff(Directionable auto direct){}
void doOtherStuff(Positionable auto direct){}

旁注,您似乎需要的感觉很像 Entity Component System pattern. In which case, EnTT 是一个很好的实现。您也可以编写自己的代码以满足更具体的需求和更小的占用空间。