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);

由于每个对象都有不同的类型和不同的期望返回类型,我将无法使用类模板参数。类型和所需的返回返回类型总是会根据我创建的对象类型而变化。

这样的事情可能吗?是否有某种设计模式可以在这种情况下有所帮助?

编辑:

设计选择的原因如下。我将调用另一个具有不同行为的函数,具体取决于接收的是 Parent* 还是 Child*。

通过取出模板参数,我认为我可以做这样的事情:

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

它可能不会改变这是一个糟糕的决定选择的事实,但我主要是出于好奇而询问。:)

4

3 回答 3

1

C++ 中的函数只能有一个返回类型,并且该类型必须在编译时已知。函数模板可以有一个依赖于它的模板参数的返回类型。由于 C++ 是静态类型的,因此必须在编译时解决依赖关系。这意味着您无法在运行时从映射中查找所需的返回类型。但是,您可以从模板参数派生它。

编辑:澄清:当您“使用”类似FunctionTemplate<Type>()orFunctionTemplate(parameterThatsUsedToDeriveTheTemplateArguments)的函数模板时,函数模板被实例化。这意味着将创建一个带有“名称”的普通函数FunctionTemplate<ARG1, ARG2, ...>- 参数 ARG1、ARG2 等的模板的所谓“特化”。这个函数就像一个普通函数,这意味着它也只能有一个固定的编译时必须知道的返回类型。/编辑

换句话说:GetObject必须至少有一个模板参数用于派生返回类型。可以工作的一件事 - 取决于您希望如何使用它 - 将在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 回答