C++ 使用c++;导出对象的协方差';s子类型

C++ 使用c++;导出对象的协方差';s子类型,c++,inheritance,covariance,C++,Inheritance,Covariance,以下代码按预期生成子类型的对象,但其默认强制转换为指向该子对象的超级指针。谁能告诉我这是为什么 class super { virtual super* get_clone() = 0; } class subA : super { sub* get_clone() { return new subA(); }; } class subB : super { sub* get_clone() { return new subB(); }; } subA a; super

以下代码按预期生成子类型的对象,但其默认强制转换为指向该子对象的超级指针。谁能告诉我这是为什么

class super {
    virtual super* get_clone() = 0;
}
class subA : super {
    sub* get_clone() { return new subA(); };
}
class subB : super {
    sub* get_clone() { return new subB(); };
}

subA a;
super *s = &a;
现在,如果我在gdb中运行s->get_clone(),我会得到:

(gdb)PS->获取克隆()
(超级*)0x7fff7c


我不明白为什么它返回(super*)指针而不是(subA*)指针。我意识到我可以强制转换指针,但我正在尝试根据返回的子类型创建模板化代码


谢谢

编译器无法知道指针在运行时将指向的对象的实际具体类是什么。它只知道指针将指向s的子类的实例

class super 
{
    public:
    virtual super* get_clone() = 0;
};

class subA : public super 
{
    public:
    subA* get_clone()
    {
        std::cout << "subA::get_clone()\n";
        return new subA();
    };
};

class subB : public super 
{
    public:
    subB* get_clone()
    {
        std::cout << "subB::get_clone()\n";
        return new subB();
    };
};

int main()
{
    subA a;
    super *s1 = &a;
    super* s2 = s1->get_clone();
    //subA* s3 = s1->get_clone();  // compile error here; the compiler doesn't know what is the concrete class s points to.

    subA* a1 = &a;
    subA* a2 = a1->get_clone();  // here it can know that the returned pointer is to subA.

}
class超级
{
公众:
虚拟超级*get_clone()=0;
};
subA类:公共超级
{
公众:
subA*get_clone()
{
std::cout get_clone();//此处出现编译错误;编译器不知道具体类s指向什么。
subA*a1=&a;
subA*a2=a1->get_clone();//这里可以知道返回的指针指向subA。
}

gdb报告的内容是正确的。它正在报告您调用的方法的返回类型。即使虚拟方法的实现具有不同的返回类型,返回值也会“转换”为您调用的方法返回的返回类型

正如在评论中指出的,虚拟方法
get_clone()的动态绑定性质
不适用于静态绑定用例。您可能希望执行的任何静态绑定都可以在子类型内完成,通过super接口。您可以使用super提供的一种CRTP样式模板功能来实现这一点:

class超级{
虚拟超级*get_clone()=0;
受保护的:
模板子*获取\克隆\公用(子*){
//...
返回新的子节点;
}
};
subA级:超级{
subA*get_clone(){返回get_clone_common(this);}
};

在上面的示例中,
subA
通过
super

提供的模板接口执行静态绑定,您的函数返回指向未定义类型的指针是否是键入错误?“但我试图基于返回的子类型创建模板化代码。”等等,您正在试图在编译时确定运行时将返回的类型?这是不可能的。不知道在编译时,
s
总是指向
subA
对象。您需要提供更多有关您真正想要完成的任务的详细信息,因为您要求的是这显然是不可能的,但几乎可以肯定的是,有些东西非常接近您所要求的,这是可能的。感谢您解释这样一个事实,即使用虚拟的协方差只相当于动态绑定,这不足以克服静态绑定。这对我来说是有意义的。但我尝试了您的建议,使用CRTP来播放从super返回值,我仍然遇到麻烦。基本上,我在get_clone中使用了gdb,get_clone的返回类型是(subA*),但是get_clone()的返回类型仍然是(super*)。看起来CRPT仍然不会更改原始(super)指针的静态强制转换。
super::get_clone()
总是会产生一个
超级*
,因为这就是代码所说的它将返回的内容。模板内容发生在
get\u clone\u common
中。
class super {
    virtual super * get_clone () = 0;
protected:
    template <typename SUB> SUB * get_clone_common (SUB *) {
        //...
        return new SUB;
    }
};

class subA : super {
    subA * get_clone () { return get_clone_common(this); }
};