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

Answers


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.


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.


Need Your Help

Configuring squid so that browser doesn't prompt for login/password

squid webproxy

At the company where I work, we have a web proxy and the browser prompts for login/pass every time I open it. After seeing this, I installed squid in my computer, configure my company web proxy as a

Connect to Amazon S3 from hosting

amazon-s3 video-streaming server-to-server

Ok I been Googling for a while now, and i can't seem to find a good tutorial on how to connect to an Amazon S3 account from my hosting server. If anyones knows of a good step by step tutorial pleas...