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.