简体   繁体   中英

Return Type Covariant

Why did invalid covariant return type error occur?

I am trying to implement a template base iterator and a derived iterator.

Code:

template <typename T>
class BaseClassA{
    public:
        virtual bool operator!=(const BaseClassA<T> & A) const {}
        virtual BaseClassA<T> operator++(T){}
} ;
template <typename T>
class DerivedClassA: public BaseClassA<T>{
    private:
        T* p;
    public:
        DerivedClassA<T>  operator++(T){
            DerivedClassA<T> tmp(*this);
            ++p;
            return tmp;
        }
        bool operator!=(const DerivedClassA<T> & A) const {
            return (A.p != p);
        } 
} ;

template <typename T>
class BaseClassB{
    private:
        BaseClassA<T> beginIter;
        BaseClassA<T> endIter;
    public:
        virtual BaseClassA<T> begin(void){}
        virtual BaseClassA<T> end(void){}
} ;

template <typename T>
class DerivedClassB{
    private:
        DerivedClassA<T> beginIter;
        DerivedClassA<T> endIter;
    public:
        DerivedClassA<T> begin(void){ return beginIter; }
        DerivedClassA<T> end(void){ return endIter; }
} ;

int main(void){
    DerivedClassB<int> B;
    B.begin() != B.end();
    ++B.begin();
}

Compiler Error (g++)

test.cpp: In instantiation of 'class DerivedClassA<int>':
test.cpp:35:26:   required from 'class DerivedClassB<int>'
test.cpp:43:24:   required from here
test.cpp:12:27: error: invalid covariant return type for 'DerivedClassA<T> DerivedClassA<T>::operator++(T) [with T = int]'
         DerivedClassA<T>  operator++(T){
                           ^
test.cpp:5:31: error:   overriding 'BaseClassA<T> BaseClassA<T>::operator++(T) [with T = int]'
         virtual BaseClassA<T> operator++(T){}
                               ^
test.cpp: In function 'int main()':
test.cpp:45:5: error: no match for 'operator++' (operand type is 'DerivedClassA<int>')
     ++B.begin();
     ^
test.cpp:45:5: note: candidate is:
test.cpp:12:27: note: DerivedClassA<T> DerivedClassA<T>::operator++(T) [with T = int]
         DerivedClassA<T>  operator++(T){
                           ^
test.cpp:12:27: note:   candidate expects 1 argument, 0 provided

C++ only directly supports covariant result type for raw pointers and raw references.

One reason is because with class types, a covariant result could need more space than the caller, knowing only a base class declaration, has set aside for that result.

The templating in the example is not relevant to this issue.


In other news:

  • You don't want a virtual operator== because you don't want runtime checking of the validity of an equality comparision, really you don't.

  • operator++() and operator++(int) are the only two valid signatures, so you can't meaningfully template the argument type.

C++ built-in covariance applies to references and pointers in the C++ object model.

Now this is C++. So if you don't like what C++ provides, you can write your own object model.

In your case you have iterators. These iterators want to be value types (because that is what C++ wants in its libraries), and you want them to be polymorphic.

Polymorphic value types are not supported naively using the C++ object model.

Using code like this or adobe poly or boost type erasure or boost any_range you can create a ducktype polymorphism system that supports value-type polymorphism.

Now your BaseClassB<T>::begin and end returns an any_iterator<T> . Things that match the concept, including DerivedClassA<T> , can be stored and manipulated within it. BaseClassA<T> becomes obsolete, as a type erasing iterator does not require a virtual base class for polymorphism.

DerivedClassB<T> also returns an any_iterator<T> . If you want naked access to the real iterators of DerivedClassB<T> have a function called get_naked_range() that returns the naked iterators of DerivedClassB<T> , which can be used in contexts where you are absolutely certain the type is DerivedClassB<T> . If you do so, also mark begin and end as final .

Note that such type erasure has runtime costs, and iterating through it will be slower than "raw naked" iteration. This only matters if you are doing this at a pretty low level in a high performance context, don't let it scare you away.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM