【问题标题】:sharing template parameters across multiple class member functions跨多个类成员函数共享模板参数
【发布时间】:2012-10-24 19:50:38
【问题描述】:

对不起,如果这个问题有点令人困惑。我真的要求学习目的,看看是否有办法实现这一点。

在下面的类中,我必须为 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));

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

【问题讨论】:

  • “对于某些对象,我希望 GetObject() 返回指向实际对象类型的指针。对于其他对象,我希望 GetObject() 返回指向父类型的指针。” 似乎是一个糟糕的设计。
  • ObjectManager 内部如何存储对象?它是将所有对象存储在一张地图中还是不同的地图中?是否可以从 ID 推断出对象类型?

标签: c++ templates


【解决方案1】:

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

编辑:澄清一下:当你“使用”像FunctionTemplate&lt;Type&gt;()FunctionTemplate(parameterThatsUsedToDeriveTheTemplateArguments) 这样的函数模板时,函数模板会被实例化。这意味着将创建一个具有“名称”FunctionTemplate&lt;ARG1, ARG2, ...&gt; 的普通函数 - 参数 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);

【讨论】:

    【解决方案2】:

    类模板呢:

    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...
    

    【讨论】:

    • 我不确定你的意思。 Object&lt;&gt; 是这里的实际对象还是更像一个对象句柄?如果它是实际对象,那么我看不到对象管理器再管理任何东西 - 这可能是 OP 需要并且无法消除的东西。如果它是一个句柄,那么这可以工作,但我建议使用另一个名称。
    • @PaulGroke 可以是对象或对象句柄。我同意/猜想 OP 想要处理。会修复的。
    【解决方案3】:

    尝试访问者模式。 Live example at 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-16
      • 1970-01-01
      • 2023-04-02
      • 2011-07-06
      • 1970-01-01
      • 2013-07-02
      相关资源
      最近更新 更多