【问题标题】:Calling derived class function from base class with templates without virtual使用没有虚拟的模板从基类调用派生类函数
【发布时间】:2013-12-16 14:15:22
【问题描述】:

我需要使用脚本生成类的一些成员/方法。我试图将这个类分成两部分,基类是生成的成员,派生类有手工编码的成员。但是,我一直在弄清楚如何从基类 B::f1() 调用派生成员函数 D::f2()

这里是简化的代码:

#include <cstdio>

template <typename _T>
class B { 
public:
    void f3() {
        puts("okay");
    }
    void f1() {   
        f2();   // What C++ Magic to call f2() properly !!!
    }
};

class D : public B<D> {
public:
    void f2() {
        f3();
    }

};

int main() {
    D d;
    d.f1();
}

有什么办法,不使用虚函数就可以从B::f1()调用D::f2()吗?

稍后添加:

如果我们进行指针操作,最终会导致注入,我知道这不是一个好主意,我会接受不这样做的建议。让我们停止那个线程。

我正在尝试仅使用模板找到解决方案。我可以生成任何复杂的东西 对于生成的代码。它甚至可以是几个仿函数等。但是手工编码 书面部分应该是可手工编码的。

【问题讨论】:

  • dynamic_cast<_t>(this)->f2(); 怎么样? (不要把这个写成答案,因为需要正确的格式和更多关于如何存在更严重问题的扩展答案)
  • @UldisK - 没有虚函数,所以 dynamic_cast 不起作用!
  • 你不应该像_T那样使用reserved names
  • @Vardhan - 如果您不想使用虚拟功能来解决此类问题,那么您将不得不使用某种折衷的解决方案。您可以避免使用类型参数将派生名称注入基类,就像我在回答中所做的那样。
  • @Vardhan 只要您调用仅在派生类中定义的f2(),您就可以将派生类“注入”到基类中。

标签: c++ templates


【解决方案1】:

如果你真的真的很想做:

static_cast<_T*>(this)->f2();

正如人们所说,这是curiously recuring template pattern

【讨论】:

  • @IvayloStrandjev - 没有虚函数,dynamic_cast 不起作用!
  • 想解释一下@Sean 的评论吗?
  • @Eric - dynamic_cast 需要 RTTI 可用才能检查演员表。此信息与 vtable 一起放置,您需要至少一个虚拟函数才能让您的类拥有 vtable。
【解决方案2】:

这是一个典型的Curiously recuring template pattern。你可以这样做:

template <typename _T>
class B { 
public:
     void f3() {
      puts("okay");
     }
     void f1() {      
      static_cast<_T>(this)->f2();
     }

};

【讨论】:

    【解决方案3】:

    【讨论】:

      【解决方案4】:

      基类不应有其子类的概念。尝试从基类调用子方法是不良架构的明确标志。顺便说一句,虚拟功能在这里对您没有帮助。

      如果您必须从基类调用子函数,您可以像在代码中的任何其他函数中那样执行此操作。您仍然需要一个基类的实例来调用它,或者您必须将static_cast this 指针指向子类类型的指针。这是一个非常糟糕的主意!

      【讨论】:

      • 这是一个已知的模式,没什么特别的。 Even referenced on isocpp blog site !
      • 虽然这是 CRTP,但我不同意反对意见。使用这种模式通常(虽然并不总是)是糟糕设计的标志。
      • @Johan 实际上通过您发布的链接阅读作者并不建议使用类似的解决方案。 Appart 来自一个好的 c++11 解决方案,他建议使用宏
      • @IvayloStrandjev 本文提出的问题要复杂得多。我只是指出这是一个众所周知的、成熟的设计。还有一个父类确实认识孩子。
      【解决方案5】:

      还有另一种方法,但代价是丑陋的 C 风格/重新解释演员表:

      #include <cstdio>
      #include <functional>
      
      
      class B { 
      public:
          typedef std::function<void (B*)> Function;    
      
          void f3() {
              puts("okay");
          }
          void f1() {   
              _func(this);
          }
      
          Function _func;
      };
      
      class D : public B 
      {
      public:
          D()
          {
              _func = (void (B::*)()) &D::f2; // Here is the awfull cast I hate to do
          }
      
          void f2() {
              f3();
          }
      
      };
      
      int main() {
          D d;
          d.f1();
      }
      

      http://ideone.com/yOR0xT

      我发现这比CRTP 不干净,因为你没有编译时间检查...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-03-19
        • 2017-02-10
        • 1970-01-01
        • 1970-01-01
        • 2013-10-07
        • 2011-09-27
        • 2021-05-09
        相关资源
        最近更新 更多