2

この質問が少し紛らわしい場合は申し訳ありません。私は本当に学習目的を求めており、これが可能になる方法があるかどうかを確認しています。

以下のクラスでは、CreateObject()とGetObject()のテンプレートパラメータを指定する必要があります。

class ObjectManager
{
public:
    template <class T>
    void CreateObject(int id)
    {
        //Store pointer to newly created object in a map
    }

    template <class T>
    T* GetObject(int id)
    {
        //Find object using id and return a T* to it
    }
};

CreateObject()は、テンプレートパラメータを使用して正しいタイプのオブジェクトを作成し、そのオブジェクトへのポインタをマップに格納します。GetObject()は、テンプレートパラメータを使用して、目的のタイプのポインタを返します。一部のオブジェクトでは、GetObject()が実際のオブジェクトタイプへのポインタを返すようにします。他のオブジェクトの場合、GetObject()が親型へのポインターを返すようにします。

今のところ、GetObject()を呼び出すときはいつでも、タイプを指定する必要があります。

SomeType* pST = m_objectManager.GetObject<SomeType>(id);
AnotherType* pAT = m_objectManager.GetObject<AnotherType>(id);

私の目標は、CreateObject()関数のテンプレートパラメーターとして、実際のオブジェクトタイプと目的のリターンタイプを指定することです。

class ObjectManager
{
public:
    template <class T, class DesiredReturnType>
    void CreateObject(int id)
    {
        //Store pointer to newly created object of type T in a map
        //The DesiredReturnType would be used by GetObject() to return the correct type of pointer
    }

    //This isn't possible but would be ideal.
    DesiredReturnType* GetObject(int id)
    {
        //Return a DesiredReturnType* to the object
    }
};

m_objectManager.CreateObject<SomeType, ParentOfSomeType>(id);

//Now I don't need to specify a template argument when calling GetObject().
//CreateObject() will create a new SomeType.
//GetObject() will return a ParentOfSomeType* which is really pointing to a SomeType object
ParentOfSomeType* pPST = m_objectManager.GetObject(id);

すべてのオブジェクトには異なるタイプと異なる目的の戻りタイプがあるため、クラステンプレートパラメーターを使用することはできません。タイプと目的のリターンリターンタイプは、作成したオブジェクトのタイプに応じて常に変化します。

このようなことは可能でしょうか?このような状況で役立つデザインパターンはありますか?

編集:

デザインを選んだ理由は以下のとおりです。親*と子*のどちらを受け取るかによって動作が異なる別の関数を呼び出します。

テンプレートの引数をとることで、次のようなことができるのではないかと思いました。

for(int id = 0; id < 10; id++)
MyFunction(m_objectManager.GetObject(id));

それが悪い決定の選択であるという事実をおそらく変えないでしょう、しかし私は主に好奇心から尋ねています。:)

4

3 に答える 3

1

C++ の関数は戻り値の型を 1 つだけ持つことができ、その型はコンパイル時に認識されている必要があります。関数テンプレートは、そのテンプレート引数に依存する戻り値の型を持つことができます。C++ は静的に型付けされるため、その依存関係はコンパイル時に解決する必要があります。つまり、実行時にマップから目的の戻り値の型を検索することはできません。ただし、テンプレート引数から派生させることはできます。

編集:明確にするために:FunctionTemplate<Type>()またはのような関数テンプレートを「使用」するとFunctionTemplate(parameterThatsUsedToDeriveTheTemplateArguments)、関数テンプレートがインスタンス化されます。これは、「名前」を持つ通常の関数FunctionTemplate<ARG1, ARG2, ...>が作成されることを意味します。つまり、引数 ARG1、ARG2 などのテンプレートのいわゆる「特殊化」です。この関数は通常の関数とまったく同じです。つまり、1 つだけ固定することができます。コンパイル時に知っておく必要がある戻り値の型。/編集

つまりGetObject、戻り値の型を導出するために使用するテンプレート引数が少なくとも 1 つ必要です。これをどのように使用するかに応じて、機能する可能性のあることの1つは、idパラメーターの型で目的の戻り値の型をエンコードすることです。

たとえば、次のようなもの

template <class T>
struct ObjectId {
    typedef T ReturnType;
    ObjectId(int id) : m_id(id) {}
    int m_id;
};

class ObjectManager {
    ...
    template <class T, class ID> // ID type will be deduced
    void CreateObject(ID id) {
        ...
    }

    template <class ID> // ID type will be deduced
    typename ID::ReturnType* GetObject(ID id) {
        ...
    }
};

...
ObjectManager obman;
auto const idFoo = ObjectId<Foo>(1);
auto const idBar = ObjectId<BarBase>(2);

obman.CreateObject<Foo>(idFoo);
obman.CreateObject<Bar>(idBar);
Foo* foo = obman.GetObject(idFoo);
BarBase* bar = obman.GetObject(idBar);
于 2012-10-24T20:11:54.213 に答える
0

クラステンプレートはどうですか:

class ObjectManager
{
public:
   /// reference to some internal object manager 
   template <class T, class DesiredReturnType>
   class ObjectRef {
   public:
     ObjectRef(ObjectManager& manager) {}
     DesiredReturnType* GetObject()
     {
     }
   };
    template <class T, class DesiredReturnType>
    ObjectRef<T,DesiredReturnType> CreateObject(int id)
    {
    }
};

auto ref = m_objectManager.CreateObject<SomeType, ParentOfSomeType>(id);

//Now I don't need to specify a template argument when calling GetObject().
//CreateObject() will create a new SomeType.
//GetObject() will return a ParentOfSomeType* which is really pointing to a SomeType object
ParentOfSomeType* pPST = ref.GetObject(); // no need to specify id...
于 2012-10-24T20:02:54.253 に答える
0

ビジターパターンを試す。Ideone.com での実例

struct ConcreteVisitor: IVisitor
{
    virtual void visit(int)
    {
        cout << "visiting int" << endl;
    }
    virtual void visit(double)
    {
        cout << "visiting double" << endl;
    }
    virtual void visit(SomeClass&)
    {
        cout << "visiting SomeClass" << endl;
    }
    // ..
};
int main(int argc,char *argv[])
{
    ObjectManager manager;
    ConcreteVisitor visitor;

    manager.create_object<int>(1);
    manager.create_object<double>(2);
    manager.create_object<SomeClass>(3);

    manager.apply(1,visitor);
    manager.apply(2,visitor);
    manager.apply(3,visitor);
    return 0;
}

出力は次のとおりです。

SomeClass::SomeClass()
visiting int
visiting double
visiting SomeClass
SomeClass::~SomeClass()

完全なコード:

#include <boost/shared_ptr.hpp>
#include <boost/ptr_container/ptr_map.hpp>
#include <iostream>
#include <ostream>
#include <map>

using namespace std;
using namespace boost;
typedef int ID;

struct SomeClass
{
    SomeClass()
    {
        cout << "SomeClass::SomeClass()" << endl;
    }
    ~SomeClass()
    {
        cout << "SomeClass::~SomeClass()" << endl;
    }
};

struct IVisitor
{
    virtual void visit(int)=0;
    virtual void visit(double)=0;
    virtual void visit(SomeClass&)=0;
    // ..
};

struct ConcreteVisitor: IVisitor
{
    virtual void visit(int)
    {
        cout << "visiting int" << endl;
    }
    virtual void visit(double)
    {
        cout << "visiting double" << endl;
    }
    virtual void visit(SomeClass&)
    {
        cout << "visiting SomeClass" << endl;
    }
    // ..
};

struct ITypeStorage
{
    virtual void apply_visitor(void *obj,IVisitor &visitor)=0;
};

template<typename ObjectType> struct TypeStorage: ITypeStorage
{
    virtual void apply_visitor(void *obj,IVisitor &visitor)
    {
        visitor.visit( *(static_cast<ObjectType *>(obj)) );
    }
};

class ObjectManager
{
    map<ID,boost::shared_ptr<void> > objects;
    ptr_map<ID,ITypeStorage> types;
public:
    template <class T>
    void create_object(ID id)
    {
        objects[id].reset(new T());//shared_ptr will use right deleter
        types.insert(id,new TypeStorage<T>());
    }
    void apply(ID id,IVisitor &visitor)
    {
        types.find(id)->second->apply_visitor(objects[id].get(),visitor);
    }
};

int main(int argc,char *argv[])
{
    ObjectManager manager;
    ConcreteVisitor visitor;

    manager.create_object<int>(1);
    manager.create_object<double>(2);
    manager.create_object<SomeClass>(3);

    manager.apply(1,visitor);
    manager.apply(2,visitor);
    manager.apply(3,visitor);
    return 0;
}
于 2012-10-24T21:37:11.880 に答える