Covariant return type

本文深入探讨了C++模板中的Covariant return type特性,解释了为何在某些情况下,子类覆盖父类虚函数的返回类型可以不同于父类,以及这一特性如何在实际编程中应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

class Shape {
  public:
    virtual double area() const = 0;
};
class Circle : public Shape {
  public:
    float area() const; // error! different return type    
};

int
main() {
}

在继承关系中,子类覆盖父类的虚函数的时候,必须连返回值类型也完全相同。所以上面这个程序编译是编不过的:
main.cpp:9: error: conflicting return type specified for `virtual float Circle::area() const'
main.cpp:5: error:   overriding `virtual double Shape::area() const'

但是这个限制在所谓的“Covariant return type”的情况下可以被放松。如果基类的一个虚函数返回值类型是B*,那么其派生类中覆盖这个函数的时候,返回值类型可以是D*,其中D是任何以public方式继承自B的派生类(也就是说D is-a B)。如果基类的虚函数返回B&,派生类覆盖这个函数的时候也可以返回D&。

例如这段程序编译就没有问题:

class Shape {
  public:
//    virtual double area() const = 0;
    virtual Shape* clone() const = 0;
};
class Circle : public Shape {
  public:
//    float area() const; // error! different return type
    virtual Circle* clone() const;
};

int
main() {
}

当程序通过指向基类(接口)的指针操纵不同的派生类(实体类)的对象时,派生类方法返回的D*/D&类型的值,可以被自动转换为B*/B&类型。

=================

看下面的例子。要点:返回值(A*,B*)涉及到的类,和虚函数所在的类(C和D),可以分属于两个完全不同的继承体系。

class A { };
class B: public A { };
class C {
  public:
    virtual A* getA() const = 0;
};
class D: public C {
  public:
    B* getA() const;
};

这段程序编译没有错误。

注意:
需要包含继承类头文件,光是类型声明是不行的。

/data/k50048780/cpp_mutator/mutators/BitStream.cpp: In member function ‘BitStream BitStream::slice(size_t, size_t) const’: /data/k50048780/cpp_mutator/mutators/BitStream.cpp:154:46: error: passing ‘const BitStream’ as ‘this’ argument discards qualifiers [-fpermissive] 154 | copy_bits(*this, start_bit, 0, bit_length); | ^ In file included from /data/k50048780/cpp_mutator/mutators/BitStream.cpp:5: /data/k50048780/cpp_mutator/mutators/BitStream.h:62:10: note: in call to ‘void BitStream::copy_bits(const BitStream&, size_t, size_t, size_t)’ 62 | void copy_bits(const BitStream& src, size_t src_start, size_t dest_start, size_t bit_count); | ^~~~~~~~~ CMakeFiles/mutator.dir/build.make:446: recipe for target 'CMakeFiles/mutator.dir/mutators/BitStream.cpp.o' failed make[3]: *** [CMakeFiles/mutator.dir/mutators/BitStream.cpp.o] Error 1 make[3]: *** Waiting for unfinished jobs.... /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp: In constructor ‘StringLengthEdgeCase::StringLengthEdgeCase(std::shared_ptr<StringBase>, std::mt19937)’: /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:10:37: error: no matching function for call to ‘IntegerEdgeCases::IntegerEdgeCases(std::shared_ptr<StringBase>&, std::mt19937&)’ 10 | : IntegerEdgeCases(obj, rand) { | ^ In file included from /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.h:10, from /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:5: /data/k50048780/cpp_mutator/mutators/string/../utils.h:146:5: note: candidate: ‘IntegerEdgeCases::IntegerEdgeCases(std::shared_ptr<NumberBase>, std::mt19937&)’ 146 | IntegerEdgeCases(std::shared_ptr<NumberBase> obj, std::mt19937 &rand); | ^~~~~~~~~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/../utils.h:146:50: note: no known conversion for argument 1 from ‘shared_ptr<StringBase>’ to ‘shared_ptr<NumberBase>’ 146 | IntegerEdgeCases(std::shared_ptr<NumberBase> obj, std::mt19937 &rand); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~ /data/k50048780/cpp_mutator/mutators/string/../utils.h:144:7: note: candidate: ‘IntegerEdgeCases::IntegerEdgeCases(const IntegerEdgeCases&)’ 144 | class IntegerEdgeCases : public Mutator { | ^~~~~~~~~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/../utils.h:144:7: note: candidate expects 1 argument, 2 provided /data/k50048780/cpp_mutator/mutators/string/../utils.h:144:7: note: candidate: ‘IntegerEdgeCases::IntegerEdgeCases(IntegerEdgeCases&&)’ /data/k50048780/cpp_mutator/mutators/string/../utils.h:144:7: note: candidate expects 1 argument, 2 provided /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp: In member function ‘virtual std::pair<long int, long int> StringLengthEdgeCase::get_limits(std::shared_ptr<StringBase>)’: /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:24:25: error: request for member ‘has_value’ in ‘((std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>*)(& obj))->std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>::operator->()->StringBase::<anonymous>.TypeBase<StringBase>::max_length’, which is of non-class type ‘int64_t’ {aka ‘long int’} 24 | if (obj->max_length.has_value()) { | ^~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:25:47: error: request for member ‘value’ in ‘((std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>*)(& obj))->std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>::operator->()->StringBase::<anonymous>.TypeBase<StringBase>::max_length’, which is of non-class type ‘int64_t’ {aka ‘long int’} 25 | max_ = std::min(max_, obj->max_length.value()); | ^~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:29:25: error: request for member ‘has_value’ in ‘((std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>*)(& obj))->std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>::operator->()->StringBase::<anonymous>.TypeBase<StringBase>::min_length’, which is of non-class type ‘int64_t’ {aka ‘long int’} 29 | if (obj->min_length.has_value()) { | ^~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:30:47: error: request for member ‘value’ in ‘((std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>*)(& obj))->std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>::operator->()->StringBase::<anonymous>.TypeBase<StringBase>::min_length’, which is of non-class type ‘int64_t’ {aka ‘long int’} 30 | min_ = std::max(min_, obj->min_length.value()); | ^~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp: At global scope: /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:42:6: error: no declaration matches ‘void StringLengthEdgeCase::perform_mutation(std::shared_ptr<StringBase>, int64_t)’ 42 | void StringLengthEdgeCase::perform_mutation(std::shared_ptr<StringBase> obj, int64_t value) { | ^~~~~~~~~~~~~~~~~~~~ In file included from /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:5: /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.h:16:10: note: candidate is: ‘void StringLengthEdgeCase::perform_mutation(std::shared_ptr<String>, size_t)’ 16 | void perform_mutation(std::shared_ptr<String> obj, size_t index); | ^~~~~~~~~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.h:13:7: note: ‘class StringLengthEdgeCase’ defined here 13 | class StringLengthEdgeCase : public IntegerEdgeCases { | ^~~~~~~~~~~~~~~~~~~~ In file included from /data/k50048780/cpp_mutator/mutators/MutatorFactory.cpp:8: /data/k50048780/cpp_mutator/mutators/number/NumberVariance.h:13:32: error: invalid covariant return type for ‘virtual std::pair<long unsigned int, long unsigned int> NumberVariance::get_limits(std::shared_ptr<NumberBase>)’ 13 | std::pair<size_t ,size_t > get_limits(std::shared_ptr<NumberBase> obj); | ^~~~~~~~~~ In file included from /data/k50048780/cpp_mutator/mutators/string/../../utils/sample_util.h:12, from /data/k50048780/cpp_mutator/mutators/string/StringCaseLower.h:8, from /data/k50048780/cpp_mutator/mutators/MutatorFactory.cpp:6: /data/k50048780/cpp_mutator/mutators/string/../../utils/../mutators/utils.h:70:51: note: overridden function is ‘virtual std::pair<long int, long int> IntegerVariance::get_limits(std::shared_ptr<NumberBase>)’ 70 | virtual std::pair<std::int64_t, std::int64_t> get_limits(std::shared_ptr<NumberBase> obj); | ^~~~~~~~~~
最新发布
06-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值