возвращающий шаблонный подкласс - PullRequest
0 голосов
/ 05 мая 2011

У меня есть куча Контейнеров и Объектов. Контейнеры и объекты имеют шаблонные подклассы. Эти шаблонные подклассы имеют специализированные подклассы. В специализированных объектах я хочу получить их контейнеры. Вот настройка кода:

class Container;
template<class T> class SubContainer;

class Object
{
public:
  Object() : m_pContainer(NULL) { }

public:
  Container* GetContainer()
  {
    return m_pContainer;
  }

  void SetContainer(Container* pContainer)
  {
    m_pContainer = pContainer;
  }

private:
  Container* m_pContainer;
};

class Container
{
public:
  Container() : m_pObject(NULL) { }
  virtual ~Container() { }

public:
  void SetObject(Object* pObject)
  {
    m_pObject = pObject;
    m_pObject->SetContainer(this);
  }

protected:
  Object* m_pObject;
};

template<class T>
class SubObject : public Object
{
public:
  virtual SubContainer<SubObject>* GetSubContainer()
  {
    return dynamic_cast<SubContainer<SubObject>*>(GetContainer());
  }

  void TestMe()
  {
    SubContainer<SubObject>* pSubContainer = GetSubContainer();
    assert(pSubContainer);
  }
};

template<class T>
class SubContainer : public Container
{
};


class SubObjectInt : public SubObject<int>
{
};

class SubContainerSubObjectInt : public SubContainer<SubObject<int> > // works
//class SubContainerSubObjectInt : public SubContainer<SubObjectInt> // fails
{
};

Тестовый код:

SubContainerSubObjectInt* pContainer = new SubContainerSubObjectInt();
SubObjectInt* pObject = new SubObjectInt();

pContainer->SetObject(pObject);

pObject->TestMe();

Я знаю, что SubContainer<SubObjectInt> НЕ является подклассом SubContainer<SubObject<int> >, хотя SubObjectInt является подклассом SubObject<int>.

Я пометил код "работает" и "не работает". Строка с надписью «fails» имеет более логичный смысл в моем коде, но я не могу получить правильный субконтейнер, который содержит его. Динамическое приведение всегда возвращает NULL.

Мой вопрос: как я могу получить правильные SubContainer с GetSubContainer() в SubObject?

Надеюсь, это имеет смысл.

Ответы [ 2 ]

2 голосов
/ 05 мая 2011

Наименьшее изменение в вашем коде будет меняться:

class SubObjectInt : public SubObject<int>
{
};

до

typedef SubObject<int> SubObjectInt;

Текущее ошибочное определение теперь скомпилируется и вернет действительный указатель.

Если вы хотите, чтобы конкретный код был специфичным для SubObjectInt, отличным от SubObject, вместо этого вы могли бы иметь:

template<class T>
class SubObject : public Object
{
public:
    typedef SubContainer<SubObject<T> > ContainerType;

    ContainerType* GetSubContainer()
    {
        Container* container = GetContainer();
        return dynamic_cast<ContainerType*>(container);
    }

    void TestMe()
    {
        ContainerType* pSubContainer = GetSubContainer();
        assert(pSubContainer);
    }
};

И тогда ваш тестовый код выглядит примерно так:

SubObjectInt::ContainerType* pContainer = new SubObjectInt::ContainerType();
SubObjectInt* pObject = new SubObjectInt();

pContainer->SetObject(pObject);

pObject->TestMe();

РЕДАКТИРОВАТЬ: в ответ на первый комментарий

ну, я бы сказал, что вам может быть лучше использовать другой дизайн, вы смешиваете наследование, композицию и шаблоны таким образом, что усложняет то, что, я думаю, вы хотите достичь.

у вас есть тип контейнера, который вы хотите иметь возможность назначать объекты. у вас есть тип объекта, который хочет знать о своем контейнере.

вы хотите, чтобы типы контейнеров и объектов выполняли одни и те же функции, а некоторые по-разному в зависимости от их содержимого.

я бы предложил что-то вроде этого:

template<class T>
class ObjectStrategy
{
public:
    virtual void execute(T* object)
    {
        std::cout << "oh noes i am a default general ObjectStrategy" << std::endl;
    }
};

template<class T>
class ContainerStrategy
{
public:
    virtual void execute(T* container)
    {
        std::cout << "oops i am a default general ContainerStrategy" << std::endl;
    }
};

template<class T>
class Object;

template<class T>
class Container
{
public:
    Container() : m_pObject(0), m_strategy(new ContainerStrategy<Container<T> >()) { }
    Container(ContainerStrategy<Container<T> >* strategy_override) : m_pObject(0), m_strategy(strategy_override) { }
    ~Container() { delete m_strategy; }

    void SetObject(T* pObject)
    {
        m_pObject = pObject;
        m_pObject->SetContainer(this);
    }

    void DoContainerStuff()
    {
        m_strategy->execute(this);
    }

protected:
    T* m_pObject;
    ContainerStrategy<Container<T> >* m_strategy;
};

template<class T>
class Object
{
public:
    Object() : m_pContainer(0), m_strategy(new ObjectStrategy<Object<T> >()) { }
    Object(ObjectStrategy<Object<T> >* strategy_override) : m_pContainer(0), m_strategy(strategy_override) { }
    ~Object() { delete m_strategy; }

    Container<Object<T> >* GetContainer()
    {
        return m_pContainer;
    }

    void SetContainer(Container<Object<T> >* pContainer)
    {
        m_pContainer = pContainer;
    }

    void DoObjectStuff()
    {
        m_strategy->execute(this);
    }

    void TestMe()
    {
        DoObjectStuff();
        Container<Object<T> >* pContainer = GetContainer();
        pContainer->DoContainerStuff();
    }
protected:
    Container<Object<T> >* m_pContainer;
    ObjectStrategy<Object<T> >* m_strategy;
};

typedef Object<int> ObjectInt;

template<>
class ObjectStrategy<ObjectInt>
{
public:
    virtual void execute(ObjectInt* container)
    {
        std::cout << "omg i am a default specific strategy for ObjectInt" << std::endl;
    }
};

typedef Container<ObjectInt> ContainerObjectInt;

template<>
class ContainerStrategy<ContainerObjectInt>
{
public:
    virtual void execute(ContainerObjectInt* container)
    {
        std::cout << "pow i am a default specific strategy for ContainerObjectInt" << std::endl;
    }
};

class ObjectIntOverrideStrategy : public ObjectStrategy<ObjectInt>
{
public:
    virtual void execute(ObjectInt* object)
    {
        std::cout << "bam i am an overriding specific strategy for ObjectInt" << std::endl;
    }
};

class ContainerObjectIntOverrideStrategy : public ContainerStrategy<ContainerObjectInt>
{
public:
    virtual void execute(ContainerObjectInt* object)
    {
        std::cout << "woo i am an overriding specific strategy ContainerObjectInt" << std::endl;
    }
};

int main(int argc, char** argv)
{
    {   // test with default + general strategies
        typedef Object<float> ObjectFloat;
        typedef Container<ObjectFloat> ContainerObjectFloat;

        ObjectFloat* pObject = new ObjectFloat();
        ContainerObjectFloat* pContainer = new ContainerObjectFloat();

        pContainer->SetObject(pObject);

        pObject->TestMe();
    }

    {   // test with default + specific strategies
        ObjectInt* pObject = new ObjectInt;
        ContainerObjectInt* pContainer = new ContainerObjectInt;

        pContainer->SetObject(pObject);

        pObject->TestMe();
    }

    {   // test with overriding + specific strategies
        ObjectInt* pObject = new ObjectInt(new ObjectIntOverrideStrategy);
        ContainerObjectInt* pContainer = new ContainerObjectInt(new ContainerObjectIntOverrideStrategy);

        pContainer->SetObject(pObject);

        pObject->TestMe();
    }

    return 0;
}

общая функциональность объектов или контейнеров реализована как функции-члены объекта или контейнера соответственно.

функциональность объектов или контейнеров для каждого типа реализована с помощью композиции объектов стратегии, вы можете использовать фабрику для производства соответствующих объектов и контейнеров на основе соответствующих стратегий.

Я предложил довольно гибкий подход (возможно, слишком гибкий), чтобы вы могли игнорировать специализацию или наследование шаблонов для объектов стратегии в зависимости от того, насколько специфичным вам является поведение.

0 голосов
/ 05 мая 2011

Вы можете попробовать кое-что по этому поводу:

class SubObjectInt : public SubObject<int>
{
  public:
  typedef SubObject<int> parent;
};

class SubContainerSubObjectInt 
: virtual public SubContainer<SubObjectInt> 
, virtual public SubContainer<SubObjectInt::parent> 
{
  public:
  void SetObject(Object* pObject)
  {
    SubContainer<SubObjectInt::parent>::SetObject(pObject);
  }
};

Чтобы быть чище, вы должны унаследовать частный от SubContainer и повторно опубликовать фактический метод, который вам нужен, как открытый член.

...