【问题标题】:Return variant type from method从方法返回变体类型
【发布时间】:2013-03-20 04:46:15
【问题描述】:

所以我有一个资源管理器,它基本上是一个 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 的目的:寻找魔法。如果不可能,我理解。但如果有人有创新的解决方案,那就太棒了。

【问题讨论】:

    标签: c++ templates trailing-return-type


    【解决方案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&lt;Type&gt;,他们就可以使用它来获取正确类型的资源,而无需显式提供@987654327 @ 参数,所以它看起来像一个普通的函数调用。

    如果可以使用正确的类型静态声明和初始化可能的 Text&lt;Type&gt; 对象集,这很容易实现,我不确定在您的情况下是否正确。相反,如果用户必须能够在运行时创建它们,那么他们将需要知道要创建的正确类型,所以它只是将问题推回了一点:但是,如果相同的话,它可能会很方便Text&lt;Type&gt; 对象会被反复使用。

    请注意,如果您或客户需要将不同的 Text&lt;Type&gt; 对象存储在某种数据结构中,您也很不走运,因为这需要某种类型的擦除(即向上转换为 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 来擦除类型信息,那么他们将负责正确恢复类型信息,方法是在检索之前向下转换密钥或在之后向下转换资源。

    请注意,这只是您无论如何都必须做的事情之外的一个额外选项:如果您想使用非类型特定的键提供对资源的访问,唯一的方法是返回一个非- 特定的资源类型(即BaseResourceboost::anyboost::variant&lt;...&gt; 或它们的道德等价物之一。)但是,创建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 版本基本上是您在没有这些模板化重载的情况下需要执行的操作,但其他两个版本是除此之外的额外选项,您可以使用这种方法而无需任何运行时成本。

    【讨论】:

    • 感谢您的帮助,但不幸的是,我无法将类型键入给定的标识符。在将对象添加到集合之前,标识符与类型无关。它不是像 RetrieveResource("Object") 这样的调用,而是像 RetrieveResource("Tree01") 那样使用。
    • @WilliamCustode 看看我的新编辑,以防万一。它基本上不比使用类型擦除变体类型差,除了它隐藏类型安全接口后面的类型擦除,只要客户端能够保留正确键入的密钥(如果他们不能,那么你'和你现在一样。)
    • 我担心我可能误解了密钥的性质。键只是稍后用于拉取它的对象的字符串名称。键和对象之间没有典型的关系(除了它用于检索对象的明显事实)。不幸的是,您提供的解决方案并没有让我比现在更进一步。不过我很欣赏你的努力!为努力和相对性点赞。
    • @WilliamCustode 当然,只是确保,至少不能保证您使用给定键放入的相同类型的对象与您想要的类型相同吗?使用此解决方案,客户可以执行auto key = manager.PutResource("Tree01", tree) 之类的操作,然后在不进行任何转换的情况下执行Tree tree = manager.RetrieveResource(key);如果他们必须使用非类型特定的键,那么他们可以使用非模板化的重载(这基本上与你必须要做的事情相同,否则。)
    • BaseText 可以只是一个字符串的薄包装器,并且可以隐式转换为/从它,就像(大概)在您当前的情况下,Text&lt;Type&gt; 只是一个特定于类型的派生类没有任何新成员。
    【解决方案2】:

    恐怕这会模棱两可。你can't overload by return type 不能使用auto 作为返回类型。

    【讨论】:

    • 是的,这就是我所在的位置。感谢您提供相关帖子的链接。
    【解决方案3】:

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

    【讨论】:

    • 谢谢;已经有一个相当强大的打字系统。但是系统没有办法有意识地为用户投射数据,这是目标。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-11
    • 2014-04-23
    • 1970-01-01
    • 1970-01-01
    • 2021-04-05
    • 2012-05-17
    相关资源
    最近更新 更多