【问题标题】:Call derived class methods from CRTP base class object从 CRTP 基类对象调用派生类方法
【发布时间】:2021-09-10 21:54:56
【问题描述】:

有没有一种方法可以从基类对象调用派生类的方法,而基类对象不提供调用接口?我想做这样的事情

template<typename T>
struct A
{
    using Derived = T;
    void print()
    {
        static_cast<T*>(this)->print_();
    }

    void print_()
    {
        std::cout << "Base" << std::endl;
    }

    void a()
    {
        std::cout << "Aa" << std::endl;
    }
};

struct B : public A<B>
{
    void print_()
    {
        std::cout << "BD" << std::endl;
    }

    void b()
    {
        std::cout << "Bb" << std::endl;
    }
};

struct C : public A<C>{
    void c()
    {
        std::cout << "Cc" << std::endl;
    }
};

int main()
{
    A<B> b;
    A<C> c;
    b.print();
    c.print();

    B bd;
    b.a();
    bd.b(); // This works
    // b.b(); // I understand this doesn't work, but I want to make this work.


    C bc;
    c.a();
    bc.c(); // Same as class A<C> and C

}

我可以提供接口来调用这些函数,但我想知道这是否可以实现。非常感谢任何有关可行性的解决方案或 cmet。

PS:我只是对此感到好奇,因为我在错误地实例化类时遇到了意外错误。

【问题讨论】:

  • template&lt;typename T&gt; struct A : T {/*..*/}; 代替?但你不能再做B{}.a()
  • 您需要使用这样的方法解决的原始和潜在问题(如果有)是什么?如果只是单纯的好奇,那没关系,只要你说出来。但是,如果您有一个应该解决的实际问题,那么请直接询问该问题,并提供您如何解决该问题的尝试。
  • T* A&lt;T&gt;::operator -&gt;() 可能是一个选项。
  • 你已经在b.print(); 有未定义的行为,因为bA&lt;B&gt;b 中不包含B)。当您转换为 B* 并调用非静态成员函数 B::print_ 时,您将在不存在的对象上调用它。所以问题是,当没有B 实例时,您希望b.print()b.b() 在什么实例上运行?
  • @Someprogrammerdude 这只是好奇。感谢您的评论,将更新问题。 :)

标签: c++ crtp


【解决方案1】:

有没有一种方法可以从基类对象调用派生类的方法,而基类对象不提供调用接口?

是的,正如 Jarod42 建议的那样,您可以在 A 中实现 operator-&gt;。您不能重载operator.,因此在调用此类方法时必须使用-&gt;

template<typename T>
struct A {
    T* operator->() { return static_cast<T*>(this); }
};

你现在可以编译这个了:

A<B> b;
b->print_();
b->b();

但是:您的程序会有未定义的行为。 b 不是 B。这是一个A&lt;B&gt;,它不继承自B,因此您将在不存在的对象上调用非static 成员函数。

我建议您避免实例化没有正确 CRTP 关系的 A:s。

template<typename T>
struct A {
    T* operator->() { return static_cast<T*>(this); }
    
private:
    A() = default; // hidden from all ...
    friend T;      // ... except T
};

您现在可以实例化 B,但不能实例化 A&lt;B&gt;C,如果有人像这样进行虚假继承:

struct X {};
struct C : A<X> {}; // C can't be instantiated. A is friend of X, not C

Demo

【讨论】:

    【解决方案2】:

    简单的例子:如果你添加一个静态函数有一个安全的方法(因为它不会访问实例日期)。

    复杂情况:b.b():这几乎有效,但绝对是个坏主意。只要函数不引用任何实例变量,它就会相对安全。但否则它肯定会崩溃。原因是b没有Binstance

          template<typename T>
      struct A
      {
         typedef typename T Derived;
         typedef typename A<T> AT;
         void print()
         {
            static_cast<T*>(this)->print_();
         }
    
         void print_()
         {
            std::cout << "Base" << std::endl;
         }
    
         void a()
         {
            std::cout << "Aa" << std::endl;
         }
    
         //these conversion operators are for b.b() case. very bad idea!
         operator Derived* () {
            return static_cast<T*>(this);
         }
    
         operator Derived& () {
            return static_cast<Derived&>(*this);
         }
    
    
    
      };
    
      struct B : public A<B>
      {
         void print_()
         {
            std::cout << "BD" << std::endl;
         }
    
         static void static_b()
         {
            std::cout << "Bb::static_b" << std::endl;
         }
    
         void b()
         {
            std::cout << "Bb::b" << std::endl;
         }
    
      };
    
      void test()
      {
         b.A<B>::Derived::static_b(); // This should work.
    
         b.AT::Derived::static_b(); // This works with "AT" but it's recursive template. Not good.
    
         ((B&)b).b(); //This works but even though operator is implicit, it cannot implicitly know what to do. Nor will "auto".
    
         //This works but even though operator is implicit, it cannot implicitly know what to do. Nor will "auto".
         B& br = b; 
         br.b();
    
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-05
      • 1970-01-01
      • 2014-03-17
      • 1970-01-01
      相关资源
      最近更新 更多