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

时间:2013-02-12 12:39:39

标签: c++ templates

根据以下代码,如何在“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 T\n" );
  }
};

因此,如果我有一个Controller类,它将调用Serialise函数并传入对象以进行序列化,我最终必须存储已知其派生类型的指针,因为它是我需要的对象类型的一部分能够使用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!
};

3 个答案:

答案 0 :(得分:1)

简短回答 - 你不能。

假设模板参数不影响Base的接口(即:Derived不出现在任何函数签名中),您可以拥有非模板Base类,派生类可以是模板。但是,这与您当前的模式完全不符。

在你的情况下,如果模板参数影响接口(我强烈怀疑它在这种情况下确实如此),那么Controller需要知道Derived以便使用 Base,所以在哪里其中的伤害也知道为了宣告基础。衍生。

评论后编辑: 您确定要任何派生类能够序列化到任何类型吗?为什么不使用派生自Serialiser基类的类的层次结构,然后Serialise()可以接受对Serialiser类型的引用并丢失模板参数。

答案 1 :(得分:0)

你做不到。但Base :: Serialise唯一能做的就是调用派生类的Serialise方法。为什么不将它设为纯虚拟,以便Base不需要模板参数?

答案 2 :(得分:0)

您似乎想要解决问题的具体方法是不可能的,因为基类的类型取决于派生类的类型。这意味着基类模板实例化不能表示为单一类型,这对于实现您正在采用的方法是必要的。

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

// 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;
};