c++;动态返回类型 我不确定这是不是一件事(老实说,我想说不是),但我想知道是否有一种方法来写C++函数,以便它可以选择返回哪种类型的对象。

c++;动态返回类型 我不确定这是不是一件事(老实说,我想说不是),但我想知道是否有一种方法来写C++函数,以便它可以选择返回哪种类型的对象。,c++,polymorphism,C++,Polymorphism,例如,我有一个基类(a),它有3个子类(Aa,Ab,Ac)。在工厂(F)类中,我有一个std::map,它根据UINT id保存了许多子类。我的目标是编写一个函数,当我传入一个id值时,它可以构建并返回正确的对象 我可能最终会返回指针并克隆它们所指向的数据,但我只是好奇上述操作是否真的可行 谢谢 当然,是这样的: class MyMapClass { public: template< class ExactType > ExactType * getValue(UINT k

例如,我有一个基类(
a
),它有3个子类(
Aa
Ab
Ac
)。在工厂(
F
)类中,我有一个
std::map
,它根据
UINT id
保存了许多子类。我的目标是编写一个函数,当我传入一个id值时,它可以构建并返回正确的对象

我可能最终会返回指针并克隆它们所指向的数据,但我只是好奇上述操作是否真的可行


谢谢

当然,是这样的:

class MyMapClass
{
public:
    template< class ExactType > ExactType * getValue(UINT key)
    {
        return dynamic_cast<ExactType*>(_myMap.at(key));
    }

    BaseType * at(UINT key)
    {
        return _myMap.at(key);
    }

private:
    std::map<UINT, BaseType*> _myMap;
}
类MyMapClass
{
公众:
模板ExactType*getValue(UINT键)
{
返回动态_cast(_myMap.at(key));
}
基本类型*at(UINT键)
{
返回_myMap.at(键);
}
私人:
std::map\u myMap;
}
但是,由于您存储的是指向基类型的指针,因此您也可以按原样返回它们,并依赖调用者进行特定转换(如果这与应用程序的体系结构相符合的话)

不幸的是,您不能完全自动完成此操作。迟早要确定隐藏在基类指针后面的确切类,并进行转换。使用模板解决方案,“更快”完成:

MyDerivedType*value=myMapClassInstance.getValue(1);
如果希望返回基指针,则在“以后”完成:

BaseType*value=myMapClassInstance.at(1);
MyDerivedType*exactValue=动态转换(值);

C++是静态类型的,必须在编译时知道函数的返回类型。由此产生了一个问题:

  • 我是否静态地知道
    F
    的每个调用站点上的预期返回类型(==它仅取决于
    常量表达式
    值)
  • 还是取决于某个运行时变量
  • 对于案例1,
    F
    的函数模板将是一个很好的方法。
    但在您的例子中,您似乎面临着#2(因为您希望返回一个依赖于
    ID
    的类型,我们可以假定它不是一个常量表达式)

    由于静态类型,如果您要编写一个函数(假设您没有重载它,因为您的输入参数似乎总是相同的),它将有一个单一的和定义良好的返回类型。基本上,您没有语法来说明您的工厂
    F
    将返回
    Aa
    Ab
    Ac
    (对于静态类型和它启用的所有编译器验证来说,这是一件非常好的事情;)

    C++解决方案:类型删除 话虽如此,您有几种类型擦除的方法,这将允许您返回隐藏在普通单一类型后面的变体类型的实例

    • 最明显的一个是从指针到派生到指针到基的转换。如果您计划主要通过其
      A
      接口使用返回的对象(即,您将调用
      A
      上定义的虚拟函数),那么它尤其有用

      这个
      A*
      可以指向从
      A
      派生的任何类型。从这里,您可以在返回的指针上调用
      公共接口上定义的每个函数。当然,如果您想调用一个只在子类上可用的操作,那么您需要知道调用站点上的确切类型,然后将指针强制转换为派生的指针,然后才能调用该操作

    • 如果您希望避免使用动态内存,一种可能的替代方法是
      boost::variant
      。代价是必须显式列出函数可能返回的所有可能类型

      boost::variant<Aa, Ab, Ac> F(ID aId);
      
      boost::variant,快速介绍语法和功能


    使用此解决方案时,应在编译时知道“ExactType”。因此,您不能在迭代其他不同类型对象的“for”循环体中调用此函数。@anxieux什么“for”循环?问题没有提到任何循环。传递到函数中的信息是一个ID值(这是有疑问的)。使用此解决方案,您必须同时传递:ID和子类名称。@anxieux,没有办法自动完成此操作,每种方法都必须早晚手动进行强制转换(使用模板)或(稍后返回基类指针并强制转换)。我非常理解这一点,我认为答案应该包括这一点。因为现在它看起来就像是“自动”解决方案。另外,在回答中可能包括使用示例?一般来说,要自己回答这样的问题,思考在运行时和编译时可用的信息是很有用的。函数返回类型(如函数签名中所述)是编译时信息,而ID是运行时信息。然而,您当然可以返回指向基类的指针,它实际上指向派生对象(因为这也是运行时信息)
    Ab get(标准:积分常数)真棒的答案,谢谢你的洞察力!我可能会使用指针选项,但是这个boost::variant概念非常有趣,我必须稍微考虑一下。
    
    BaseType * value = myMapClassInstance.at(1);
    MyDerivedType * exactValue = dynamic_cast<MyDerivedType*>(value);
    
    A* F(ID aId)
    
    boost::variant<Aa, Ab, Ac> F(ID aId);