当我学习在C++中重写规则的方法时,我一直在阅读关于协变类型的文章,它似乎基本上是子类或派生类(至少是关于C++)的一个花哨的词。我做了一些测试,发现有一个令人惊讶的例外,我不明白。如下所示:
struct Animal {};
struct Cat : Animal{};
Cat gCat;
Animal* getAnimalPointer() { return &gCat; } // Returning covariant type by pointer, OK
Animal& getAnimalReference() { return gCat; } // Returning covariant type reference, OK
Animal getAnimalCopy() { return gCat; } // Returning covariant type by copy, OK
// All good
// Now testing virtual method overriding by using C++ feature of
// allowing overriding virtual methods using covariant return types
struct AnimalShelter
{
virtual Animal* getAnimalPointer() {};
virtual Animal& getAnimalReference() {};
virtual Animal getAnimalCopy() {}
};
struct CatShelter : AnimalShelter
{
Cat* getAnimalPointer() override; // Returning covariant type by pointer, OK
Cat& getAnimalReference() override; // Returning covariant type by reference, OK
Cat getAnimalCopy() override; // Returning covariant type by copy, fail
/* Visual Studio error: return type is not identical to nor covariant with
return type "Animal" of overriden virtual function CatShelter::getAnimalCopy*/
};编辑:只有在虚拟情况下,C++才能阻止您按副本返回协变类型,请参阅Fire关于可能的原因的出色答案。还有另一个类似的问题,在注释中有有趣的讨论,讨论的原因是否是调用方不知道在虚拟调用情况下为返回类型分配多少空间。
overriding virtual function return type differs and is not covariant
发布于 2017-08-25 15:37:46
C++中的协变返回类型意味着重写的返回类型必须是引用或指针。
值类型可以有不同的大小,即使它们共享一个公共基。作为指针或引用通常是相同的,或者最多是字节移位(虚拟继承或多重继承)。但是,转换值类型可能会导致切片,因为通过副本将较大的类型转换为较小的类型(即使基类型定义了副本构造函数或操作符,它们仍然经常是切片的,因为没有地方存储所添加的派生类的任何额外字段)。
例如,假设这是允许的,我有两种类型的A和B,我想使用它们作为来自X和Y的协变量返回。
struct A
{
int x, y;
};
struct B : A
{
int c;
};
class X
{
public:
virtual A get_a();
};
class Y : public X
{
public:
B get_a()override;
}这里的问题是,当使用对X的引用(实际上可能是Y )时,我可以这样做:
X *x = new Y();
A a = x->get_a();但是,通过对实际返回一个get_a的Y实例调用B,它必须隐式地将B转换为A,但这将“分割”我的B::c成员,这可能会使它处于无效状态(尤其是如果A有任何虚拟函数,以及那些现在“在”对象之外的预期的B::c函数)。
在一般情况下,程序员或编译器都无法判断这可能发生在A a = x->get_a()行中,因为X可以被任何东西(甚至是编译器潜在知识以外的信息,例如在单独的DLL中!)脱节。
在非虚拟的情况下,编译器和程序员可以告诉它的发生,所以尽管C++确实允许切片,但至少有关于它正在发生的知识,并且很可能是编译器的警告。
class X
{
public:
A get_a();
};
class Y : public X
{
public:
B get_a(); // Not an override!
}
X *x = new Y();
A a = x->get_a(); // still called X::get_a, no slice ever!
Y *y = new Y();
A a = y->get_a(); // calls Y::get_a, which slices, but the compiler and programmer can tell that from static typing.https://stackoverflow.com/questions/45884804
复制相似问题