除了其他答案之外,您还可以执行以下操作,假设每个Text
键都唯一地对应于具有已知不同的资源Type
:
class BaseText
{
// constructors that take const char *, std::string, etc.
// .. whatever else you currently have in class Text
};
template<typename Type>
class Text : public BaseText
{
// constructors that take const char *, std::string, etc.
};
然后更改ResourceManager
为:
class ResourceManager
{
template<typename Type>
Type RetrieveResource( Text<Type> name );
};
请注意,这仍然需要编译时多态性,但这意味着只要您的用户可以获得正确类型的版本Text<Type>
,他们就可以使用它来获得正确类型的资源,而无需Type
再次显式提供参数,所以看起来就像一个普通的函数调用。
如果Text<Type>
可以使用正确的类型静态声明和初始化一组可能的对象,这很容易,我不确定在你的情况下是否正确。相反,如果用户必须能够在运行时创建它们,那么他们将需要知道要创建的正确类型,所以它只是将问题推得更远:但是,如果相同的话,它可能会很方便Text<Type>
对象将被一遍又一遍地使用。
请注意,如果您或客户需要将不同的Text<Type>
对象一起存储在某种数据结构中,您也很不走运,因为这将需要某种类型的擦除(即向上转换为BaseText
)。因此,这在某种程度上是一种专门的解决方案,但它可能很有用,具体取决于事物的结构(如果没有看到更多代码,我真的无法知道)。
编辑:根据下面的评论,对象的类型似乎是由客户端在插入对象时确定的,因此您可以执行以下操作:
class ResourceManager
{
BaseText PutResource( const BaseText& name, BaseResource& resource );
template<typename Type>
Text<Type> PutResource( const BaseText& name, Type& resource );
BaseResource& RetrieveResource( const BaseText& name );
template<typename Type>
Type& RetrieveResource( const Text<Type>& name );
};
因此,客户端需要保留唯一的类型化密钥对象,以便稍后检索资源;如果他们选择通过向上转换回类型来擦除它BaseText
,那么他们将负责正确恢复类型信息,方法是在检索之前向下转换密钥或在之后向下转换资源。
请注意,这只是您无论如何都必须做的事情之外的一个额外选项:如果您想使用非特定类型的键提供对资源的访问,唯一的方法是返回非特定资源type (即BaseResource
, boost::any
, boost::variant<...>
, 或它们的道德等价物之一。)但是,创建特定于类型的子类BaseText
允许您使用完全相同的语法(即不需要显式模板参数)获得正确类型的资源。此外,这允许以下用法:
// tree is a resource of type `Tree`
auto key = manager.PutResource("Tree01", tree);
// ...
const BaseText& basekey = key; // lose type information
// ...
// these are all equivalent
Tree& tree = dynamic_cast<Tree&>(manager.RetrieveResource("Tree01"));
Tree& tree = dynamic_cast<Tree&>(manager.RetrieveResource(basekey));
Tree& tree = manager.RetrieveResource(key); // no casts required
Tree& tree = manager.RetrieveResource<Tree>("Tree01")
在没有这些模板化重载的情况下,这些dynamic_cast
版本基本上是您需要做的,但其他两个版本是除此之外的额外选项,您可以使用这种方法而无需任何运行时成本。