使用继承时,带有 CRTP 的类型定义不起作用

typedef with CRTP doesn't work when inheritance is used

本文关键字:类型 定义 不起作用 CRTP 带有 继承      更新时间:2023-10-16

是否有任何方法可以使用CTRP为继承关系中的类定义具有相同名称的类型?我尝试了以下代码,但从clang++得到了error: member 'ptr_t' found in multiple base classes of different types

#include <iostream>
#include <tr1/memory>
template <typename T> class Pointable {
public:
    // define a type `ptr_t` in the class `T` publicly
    typedef std::tr1::shared_ptr<T> ptr_t;
};
class Parent : public Pointable<Parent> {
public:
    Parent() {
        std::cout << "Parent created" << std::endl;
    }
    ~Parent() {
        std::cout << "Parent deleted" << std::endl;
    }
};
class Child : public Parent,
              public Pointable<Child> {
public:
    Child() {
        std::cout << "Child created" << std::endl;
    }
    ~Child() {
        std::cout << "Child deleted" << std::endl;
    }
};
int main(int argc, char** argv)
{
    Child::ptr_t child_ptr(new Child());
    Parent::ptr_t parent_ptr(new Parent());
    return 0;
}

当然,下面的一个是可以的(但它是多余的,违背了DRY原则)。

class Parent {
public:
    typedef std::tr1::shared_ptr<Parent> ptr_t;
    Parent() {
        std::cout << "Parent created" << std::endl;
    }
    ~Parent() {
        std::cout << "Parent deleted" << std::endl;
    }
};
class Child : public Parent {
public:
    typedef std::tr1::shared_ptr<Child> ptr_t;
    Child() {
        std::cout << "Child created" << std::endl;
    }
    ~Child() {
        std::cout << "Child deleted" << std::endl;
    }
};

如果没有办法通过使用CRTP来实现这种行为,为什么禁止这样做?

您的问题与CRTP无关,而是与多重继承有关。Child从两个基类继承了ptr_t,并且两种类型都不同:shared_ptr<Parent>shared_ptr<Child>。因此,编译器无法弄清楚mainChild::ptr_t所指的类型。

正如您所指出的,您必须使用Child中的typedef手动修复此问题(不过会使Pointable基类变得无用)。

class Child : public Parent,
              public Pointable<Child> {
public:
    typedef Pointable<Child>::ptr_t ptr_t;

假设Child是从Parent公开派生的,如果不在Child的定义中添加一些内容,就无法在每个类型中以不同的方式定义相同的typedef。无论您如何定义继承,Child都将从Parent继承错误的typedef。

一种可能性是定义一个特征类

  template<typename T> class Traits
  {
  public:
      typedef std::shared_ptr<T> ptr_t;
  }

显然,在这种情况下,这并没有给您带来什么好处,因为引用Traits::ptr_t比Child::ptt_t长。(但是,如果您有很多typedef,或者您希望以后能够更改指针类型,那么它可能会很有用。)

如果Child仅从Parent派生(而不是从Pointable显式派生),则它是Pointable,确切地说是Pointabe<Parent>,因为它是Parent,而Parent是Pointable。

Parent::ptr_t可以保存Child的实例,导致Child是Parent(至少在代码的意义上)。

我不知道你想用ptr_t做什么。您不确定确切的类型,但可以尝试在层次结构中执行dynamic_cast。也许这就足够了。