首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有返回基于模板的对象的方法的嵌套类中存在“无效协变返回类型”错误

带有返回基于模板的对象的方法的嵌套类中存在“无效协变返回类型”错误
EN

Stack Overflow用户
提问于 2013-06-23 03:11:44
回答 3查看 14.7K关注 0票数 6

下面的C++代码在编译时会给出以下错误:

代码语言:javascript
复制
covariant.cpp:32:22: error: invalid covariant return type for ‘virtual Q<B> C::test()’
covariant.cpp:22:22: error:   overriding ‘virtual Q<A> B::test()’

我不想将行virtual Q<B> test() {}更改为virtual Q<A> test() {},尽管它删除了编译错误。有没有别的办法来解决这个问题?

代码语言:javascript
复制
template <class T>
class Q
{
    public:
        Q() {}
        virtual ~Q() {}
};

class A
{
    public:
        A() {}
        virtual ~A() {}    
};

class B
{
    public:
        B() {}
        virtual ~B() {}

        virtual Q<A> test() = 0;

};

class C : public B
{
    public:
        C() {}
        virtual ~C() {}

        virtual Q<B> test() {}
};
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-06-23 03:20:30

Q<B>Q<A>是不相关的类。假设您是调用test()B的客户端:如果您不知道结果将具有什么类型,那么您将结果分配给什么?

虽然Q<A>Q<B>都是同一个类模板的实例,但这并不能改变它们是两个完全不相关的类的事实,它们可能具有完全不同的布局(由于模板专门化)。

这与执行以下操作没有什么不同:

代码语言:javascript
复制
struct X
{
    virtual std::string test() = 0;
};

struct Y : X
{
    virtual int test() { return 42; } // ERROR! std::string and int are
                                      // unrelated, just as Q<A> and Q<B>
};

客户端在指向X的指针上调用test()时,期望结果是string,但是“哇!”,该指针所指向的对象是Y类型,而Y::test()的返回类型是int。应该发生什么?运行时崩溃?

代码语言:javascript
复制
Y y;
X* p = &y;
std::string s = p->test(); // D'OH!

C++是一种静态类型语言,这意味着类型检查是在编译时执行的。在这种情况下,来自编译器的消息告诉您派生类不符合派生类的接口。

如果你想知道“无效的协变返回类型”是什么意思,特别是“协变”这个词,这很容易解释。

假设您有一个带有虚拟函数foo()的基类B,该函数返回一个X*

代码语言:javascript
复制
struct B
{
    virtual X* foo();
};

假设您有一个从B派生的类D,它通过返回一个Y*覆盖了foo(),其中Y是从X派生的类

代码语言:javascript
复制
struct D : B
{
    virtual Y* foo();
};

这是一个问题吗?好的,正确的答案来自于回答这个稍微好一点的问题:“对于调用foo()并期望返回X*的客户端来说,这会有问题吗?”

这个问题的答案显然是“否”,因为YX的派生类,所以您可以返回指向Y的指针,而不是指向X的指针

代码语言:javascript
复制
D d;
B* b = &d;
X* p = b->foo(); // Returns an Y*, but that's OK, because a pointer to Y can be
                 // assigned to a pointer to X

这是一个协变返回类型的示例。在您的示例中,相对于B::test()的返回类型,C::test()的返回类型不是协变的。

票数 10
EN

Stack Overflow用户

发布于 2013-06-23 03:19:32

签名为B::test(void)的函数返回Q<A>类型的对象,而C::test(void) (签名相同,因此您将覆盖该函数)返回Q<B>。我认为这是不可能的。

据我所知,通过返回类型重载函数是不可能的,父函数的重写需要坚持相同的返回类型。

来自标准§10.3/7

重写函数的返回类型应与重写函数的返回类型相同或与函数的类协变。如果函数D::f覆盖函数B::f,则函数的返回类型在满足以下条件时是协变的:

  • 都是指向类的指针,都是对类的左值引用,或者都是对classes112

的右值引用,

  • 返回类型B::f中的类与D::f返回类型中的类是同一个类,or是D::f
  • 返回类型中的类的明确且可访问的直接或间接基类指针或引用都具有相同的cv-限定,并且D::f返回类型中的类类型具有与B::f返回类型中的类类型相同或更少的cv-限定。
票数 1
EN

Stack Overflow用户

发布于 2013-06-23 03:21:04

你不能这么做。虚函数的重写不能更改函数的原型,除非是非常特殊的情况,例如协变返回类型。

如果要在虚拟覆盖中返回在虚拟基中返回的类型的子类,则协变返回将是有效的。但是您的Q<A>Q<B>通过继承是不相关的。BA的子类这一事实在这里没有任何区别。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17254381

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档