存储指向具有模板函数的基对象的指针

Storing a pointer to a base object that has template functions

本文关键字:函数 对象 指针 存储      更新时间:2023-10-16

基于以下代码,如何在"Controller"类中存储指向Base的指针?

template< class Derived >
class Base
{
public:
  template < typename T >
  void Serialise( T* t )
  {
    Derived* d = static_cast< Derived* >( this );
    d->Serialise( t );
  }
};
class Derived : public Base< Derived >
{
public:
  template < typename T >
  void Serialise( T* t )
  {
    printf( "serialising to object Tn" );
  }
};

因此,如果我有一个Controller类,它将调用Serialize函数并传入要串行化的对象,我最终不得不存储指针及其派生类型,因为它是对象类型的一部分,而我需要的是能够在不知道其实际类型是什么的情况下使用Base类型:

class Controller
{
public:
  void DoSerialise();
private:
  Base< Derived >* m_myObject; // I want this to just be Base* m_myObject but cant due to template!
};

简短的回答-你不能。

假设template参数不影响Base的接口(即:Derived没有出现在任何函数签名中),则可以有一个非模板基类,并且派生类可以是模板。然而,这根本不符合你目前的模式。

在您的情况下,如果模板参数确实影响了接口(我强烈怀疑在这种情况下确实影响了),那么控制器需要了解Derived才能使用Base,那么为了声明Base,它知道Derived的危害在哪里呢。

评论后编辑:是否确实希望任何派生类能够串行化为任何类型?为什么不拥有从Serialiser基类派生的类的继承结构,那么Serialise()就可以接受对类型Serialiser的引用并丢失模板参数。

你不能那样做。但是Base::Serialize所做的唯一一件事就是调用派生类的Serialize方法。为什么不让它成为纯虚拟的,这样Base就不需要模板参数了?

由于基类的类型取决于派生类的类型,因此您似乎不可能采用特定的方法来解决问题。这意味着基类模板实例化不能表示为单个类型,这对于实现您所采用的方法是必要的。

然而,这似乎是一个乞求访问者模式的问题。访问者模式可以在不进行投射的情况下进行双重调度,这正是您想要的。这里有一个可能的解决方案:

// First declare a base serializable interface that accepts a serializer.
struct ISerializer;    
struct ISerializable {
  virtual void accept_serializer(ISerializer const &) const = 0;
};
// Now declare a base serializer type that can accept any serializable type.
struct Serializable1;
struct Serializable2;
...
struct ISerializer {
  virtual void serialize(Serializable1 const &) = 0;
  virtual void serialize(Serializable2 const &) = 0;
  ...
};
// Then implement your concrete serializable types to accept the serializer and
// invoke it on themselves.
struct Serializable1 : public ISerializable {
  void acceptSerializer(ISerializer const &s) const {
    s.serialize(*this);
  }
};
struct Serializable2 : public ISerializable {
  void acceptSerializer(ISerializer const &s) const {
    s.serialize(*this);
  }
};
// You can actually be a bit more clever to eliminate redundant code:
template<typename DerivedT>
struct SerializableAdapter {
  void acceptSerializer(ISerializer const &s) const {
    s.serialize(*static_cast<DerivedT const *>(this));
  }
};
struct Serializable1 : public SerializableAdapter<Serializable1> {
};
struct Serializable2 : public SerializableAdapter<Serializable2> {
};
// Finally, implement your concrete serializers, including one function for each
// serializable type.
struct Serializer1 : public ISerializer {
  void serialize(Serializable1 const &s) const {
    ...
  }
  void serialize(Serializable2 const &s) const {
    ...
  }
};
struct Serializer2 : public ISerializer {
  void serialize(Serializable1 const &s) const {
    ...
  }
  void serialize(Serializable2 const &s) const {
    ...
  }
};
// Now you can store the serializers through the base interface.
struct Controller {
  void doSerialize(ISerializable &p_serializable) {
    p_serializable.acceptSerializer(*m_serializer)
  }
private:
  ISerializer *m_serializer;
};