具有固定实现的虚函数不使用大多数派生类 for (*this)

发布于 2024-11-07 05:35:29 字数 803 浏览 0 评论 0原文

假设我有以下代码:

struct Z;

struct A
{
  virtual void Do (Z & z) const;
};

struct B : public A {};

struct Z
{
  void use (A const & a) {}
  void use (B const & b) {}
};


void A::Do(Z& z) const{
  z.use(*this);
}

现在,当我调用 B.do 时,this 的类型是 A,这是有道理的,因为do的实现是在A中定义的。

有没有办法让对 B.do 的调用使用 use (B const &) 而不必为 do 复制粘贴相同的代码> 从AB?在我的实际代码中,我有大约 15 个(并且还在不断增加)从某个基类派生的类,每次都必须为 do 复制粘贴相同的代码似乎很浪费。

[编辑]澄清:所有Do所做的就是调用use,没有别的。 Douse 是接受和使用。访问访问者模式中的函数。

Let's say I have the following code:

struct Z;

struct A
{
  virtual void Do (Z & z) const;
};

struct B : public A {};

struct Z
{
  void use (A const & a) {}
  void use (B const & b) {}
};


void A::Do(Z& z) const{
  z.use(*this);
}

Right now, when I call B.do, the type of this is A, which make sense, because the implementation of do is defined in A.

Is there any way to have calls to B.do use use (B const &) without having to copy-paste the same code for do from A into B? In my actual code I have about 15 (and growing) classes derived from some base class and it seems a waste having to copy-paste the identical code for do everytime.

[Edit] Clarification: all Do does is call use, nothing else. Do and use are the accept & visit functions from the Visitor pattern.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

很糊涂小朋友 2024-11-14 05:35:29

既然您现在澄清了您想要的是访问者模式,那么抱歉,但事实就是如此。 此答案展示了双重调度的访问者模式的工作原理。


我想到了一个使用 CRTP 的好方法,但这可能适合你,也可能不适合你,具体取决于具体情况。
(注意:我使用了链接答案中的代码,因此名称不匹配,但我希望您明白。)

// your Z
class Visitor;

// superclass needed for generic handling
struct Superbase{
  virtual void Accept(Visitor& v) = 0;
};

// your A
template<class Der>
class Base : public Superbase{
public:
    void Accept(Visitor& v){
        v.Visit(static_cast<Der&>(*this));
    }
};

// your B
class Derived1 : public Base<Derived1> {
};

// new C
class Derived2 : public Base<Derived1> {
};

class Visitor {
public:
    virtual void Visit(Superbase& sup){
      // generic handling of any Superbase-derived type
    }

    virtual void Visit(Derived1& d1){
      // handle Derived1
    }

    virtual void Visit(Derived2& d2){
      // handle Derived1
    }
};

int main(){
    Visitor v;
    Derived1 d1;
    d1.Accept(v);
}

唯一的问题:现在您错过了机会拥有任何类型的 A 的通用句柄,因为函数不能同时是虚拟函数和模板函数。 :|
刮了一下,找到了使用 Superbase 基类的解决方案。 :) 这甚至允许您拥有 Superbase 的容器并充分利用多态性。 :)

Since you now clarified that what you want is the visitor pattern, well, sorry, but that's just how it is. This answer shows how the visitor pattern with double dispatch works.


I thought of a nice way using CRTP, but this may or may not work for you, depending on the circumstances.
(Note: I used the code from the linked answer, so the names don't match, but I hope you get the idea.)

// your Z
class Visitor;

// superclass needed for generic handling
struct Superbase{
  virtual void Accept(Visitor& v) = 0;
};

// your A
template<class Der>
class Base : public Superbase{
public:
    void Accept(Visitor& v){
        v.Visit(static_cast<Der&>(*this));
    }
};

// your B
class Derived1 : public Base<Derived1> {
};

// new C
class Derived2 : public Base<Derived1> {
};

class Visitor {
public:
    virtual void Visit(Superbase& sup){
      // generic handling of any Superbase-derived type
    }

    virtual void Visit(Derived1& d1){
      // handle Derived1
    }

    virtual void Visit(Derived2& d2){
      // handle Derived1
    }
};

int main(){
    Visitor v;
    Derived1 d1;
    d1.Accept(v);
}

The only problem: Now you're missing the chance to have a generic handle to any type of A, since functions can't be both virtual and templates. :|
Scrape that, found a solution using a Superbase base class. :) This even allows you to have a container of Superbases and take full advantage of polymorphism. :)

浅笑依然 2024-11-14 05:35:29

我认为这段代码可以满足您的要求:

#include <iostream>

struct A;
struct B;

struct Z
{
    void use (A const & a);
    void use (B const & b);
};

template<typename DERIVED>
struct XX
{
    void Do(Z& z){
        Do(z,THIS());
    }
private:
    const DERIVED& THIS() const { return static_cast<const DERIVED&>(*this); }
    void Do(Z& z, const DERIVED& t){
        z.use(t);
    }
};

struct A : public XX<A> {};
struct B : public XX<B> {};

void Z::use (A const & a) { std::cout << "use for A" << std::endl; }
void Z::use (B const & b) { std::cout << "use for B" << std::endl; }

int main(){

    A a;
    B b;
    Z z;
    a.Do(z);
    b.Do(z);
    return 0;
}

代码中唯一的“维护”或“样板”部分是从根据您自己的类型模板化的模板类派生的。

I think this code does what you want:

#include <iostream>

struct A;
struct B;

struct Z
{
    void use (A const & a);
    void use (B const & b);
};

template<typename DERIVED>
struct XX
{
    void Do(Z& z){
        Do(z,THIS());
    }
private:
    const DERIVED& THIS() const { return static_cast<const DERIVED&>(*this); }
    void Do(Z& z, const DERIVED& t){
        z.use(t);
    }
};

struct A : public XX<A> {};
struct B : public XX<B> {};

void Z::use (A const & a) { std::cout << "use for A" << std::endl; }
void Z::use (B const & b) { std::cout << "use for B" << std::endl; }

int main(){

    A a;
    B b;
    Z z;
    a.Do(z);
    b.Do(z);
    return 0;
}

The only 'maintenance' or 'boiler-plate' part of the code is to derive from the template class templated on your own type.

千秋岁 2024-11-14 05:35:29

您需要根据 this 指向的类型分派 use 的调用,因此您需要向 A添加另一个虚拟函数>B 只需调用正确的use。我假设 do 除了调用 use 之外还做了其他事情,否则你确实必须在每个子类中重新实现 do 。它看起来像这样

struct A
{
  virtual void Do (Z & z) const
  {
    // do stuff

    use(z);

    // do more stuff
  }

  virtual void use(Z & z) const
  {
    z.use(*this);
  }
};

struct B : public A
{
  virtual void use(Z & z) const
  {
    z.use(*this);
  }
};

struct Z
{
  void use (A const & a) {}
  void use (B const & b) {}
};

You need to dispatch the call of use based on the type pointed to by this so you need to add another virtual function to A and B that simply invokes the correct use. I assume that do does other things than call use of course otherwise you would indeed have to re-implement do in each subclass. It would look like this

struct A
{
  virtual void Do (Z & z) const
  {
    // do stuff

    use(z);

    // do more stuff
  }

  virtual void use(Z & z) const
  {
    z.use(*this);
  }
};

struct B : public A
{
  virtual void use(Z & z) const
  {
    z.use(*this);
  }
};

struct Z
{
  void use (A const & a) {}
  void use (B const & b) {}
};
梦归所梦 2024-11-14 05:35:29

我想我必须让你失望并且说不。这是您必须做出的权衡,以便将接口从您的类分解到访问者中。访问者必须知道哪一个向它汇报,只要你不重写基类中的虚拟Do(),访问者就会把你当作A。

请有人证明我错了! (我还看到这个解决方案以消除冗余)

I think I have to disappoint you and say no. This is the trade off you have to make, in order for you to break out the interface from your classes into the visitor. The visitor must know which one is reporting to it, as long as you don't override the virtual Do() in the base class, the visitor will treat you as A.

Please someone prove me wrong! (I'd also see this solved to remove redundancy)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文