在这种情况下,对于具有类型约束的虚拟函数模板,是否有任何解决方法?

Is there any workaround for a virtual function template with a type constraint in this case?

提问人:JensB 提问时间:10/5/2023 最后编辑:JensB 更新时间:10/5/2023 访问量:72

问:

如果你有一个概念和一个类成员函数模板,如下所示:

template<typename T>
concept Vector2 = requires (T t) { t.x; t.y; };

struct Shape
{
    bool contains(const Vector2 auto&) { ... }
};

有没有办法,通过一些解决方法,成为一个纯粹的虚拟功能?因此,子类(例如,、、等)可以像这样继承:containsCirclePolygon

struct Circle : public Shape
{
    bool contains(const Vector2 auto&) const override;
};

显然,上述方法不起作用,因为不允许使用虚拟功能模板。 这个概念的原因是我使用来自不同库的 2D 功能,并且我希望它们各自的类能够传递给我的函数。这种设计模式本身是否是一个好主意,这不是我的问题,而只是我想要实现的东西是否可能。Vector2Vector2

我四处搜索了一下,找到了一个建议基本访客模式的 SO 答案,但我很难看出我如何在这里应用它。

有没有办法实现接近我想要的东西,或者只是不可能?

(这是我之前一个问题的修改和更具体的版本。我被告知需要更多的信息和一个实际的例子来给出答案。

C++ 模板 多态性 函数模板

评论

0赞 Eljay 10/5/2023
可以改用 CRTP 吗?C++ 23有一个叫做“推断”的功能(链接是Sy “Tartan Llama” Brand写的一篇不错的文章)。你能使用 C++23 吗?可能太前沿了,但有些人站在最前沿。
0赞 HolyBlackCat 10/5/2023
如果你需要运行时多态性(即不能使用 CRTP),你无论如何都需要在某个时候将参数转换为一对数字,并且数字的类型必须是固定的。然后,您可以在调用函数之前执行此操作,可能方法是将虚拟函数设为私有,并在顶部添加模板公共包装器。

答:

0赞 Jarod42 10/5/2023 #1

您可以键入 erase 模板函数的参数。

由于您希望使用库中的对象来调用它,因此无法更改层次结构,因此可以使用 Sean Parent 模式,如下所示:

template <typename T>
class Vector2View
{
private:
 struct IVector2 // internal erased type
 {
    virtual ~IVector2() = default;
    virtual T getX() const = 0;
    virtual void setX(T value) = 0;
    virtual T getY() const = 0;
    virtual void setY(T value) = 0;
 };

  template<typename U>
  struct Vector2T : IVector2 // Wrapper around given type
  {
    explicit Vector2T(U& u) : u(u) {}
    T getX() const override { return u.x; }
    void setX(T value) override { u.x = value; }
    T getY() const  override { return u.y; }
    void setY(T value) override { u.y = value; }

    U& u;
  };

public:
  template <typename U>
  requires (Vector2<U>)
  /* implicit */ Vector2View(U& v) : ptr(std::make_unique<Vector2T<U>>(v)) {}

    T getX() const { return ptr->getX(); }
    void setX(T value) { return ptr->setX(value); }
    T getY() const { return ptr->getY(); }
    void setY(T value) { return ptr->setY(value); }

private:
  std::unique_ptr<IVector2> ptr;
};

然后

struct Shape
{
    virtual ~Shape() = default;
    virtual bool contains(const Vector2View<double>&) const = 0;
};

演示

评论

0赞 JensB 10/9/2023
此处使用堆内存是否会对性能关键型应用程序(如游戏)不利?即,每次调用函数 using 时分配并销毁一个实例(即 -reference)。Vector2TUVector2View
0赞 Jarod42 10/10/2023
您可以调整代码以使用自定义分配器(可能使用自定义池),或者在类中放置缓冲区以避免头分配,或者使用指针函数和数据(因此主要内联 vtable)交换虚拟类。