rxu rxu - 2 months ago 26
C++ Question

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

Answer

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.