0

所以我有一个资源管理器,它基本上是一个 void* 对象的字典,其中包含一些描述其类型的元数据(基本元数据,如字符串)。我现在有一个返回对象的模板方法:

class ResourceManager
{
    template<typename Type>
    Type RetrieveResource( Text name );
};

我的问题是如何使此方法不是模板方法?我已经对尾随返回类型和使用那种时髦的语法进行了一些研究。这个想法是,在某些时候,我将数据作为正确的类型,并且我想将其作为该类型返回,而无需在最终用户方面进行额外的转换。

我正在拍摄这样的东西:

auto RetrieveResource( Text name)
{
    return _dictionary[ name ]; // There's more to this, but imagine it returns varying type objects.
}

我已经考虑过实现 Boost::Any 类型的对象,但它有点复杂(不是我懒惰)。我试图避免最终用户的模板语法。

任何帮助都会很棒。此外,我不能使用变体或任何(项目规范)之类的 Boost 库结构。提前致谢!

我知道我在这里要求魔法,但我认为这就是 S/O 的目的:寻找魔法。如果不可能,我理解。但是,如果有人有一个创新的解决方案,那就太棒了。

4

3 回答 3

1

除了其他答案之外,您还可以执行以下操作,假设每个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版本基本上是您需要做的,但其他两个版本是除此之外的额外选项,您可以使用这种方法而无需任何运行时成本。

于 2013-03-19T18:12:29.410 回答
0

恐怕这会模棱两可。您不能通过返回类型重载,也不能auto用作返回类型。

于 2013-03-19T17:45:35.340 回答
0

我认为,最好的选择是返回一些基类指针。缺点是你强迫任何人从中派生,但是使用基类的智能架构可能会有更多的优点

于 2013-03-19T17:57:30.463 回答