返回类型协变

时间:2016-09-19 18:21:01

标签: c++ templates inheritance iterator

为什么会发生invalid covariant return type错误?

我正在尝试实现模板基迭代器和派生迭代器。

代码:

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();
}

编译器错误(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

2 个答案:

答案 0 :(得分:1)

C ++只直接支持原始指针和原始引用的协变结果类型。

一个原因是因为对于类类型,协变结果可能需要比调用者更多的空间,只知道基类声明,为此结果留出了空间。

示例中的模板与此问题无关。

在其他新闻中:

  • 您不需要虚拟operator==,因为您不希望运行时检查相等比较的有效性,实际上您不需要。

  • operator++()operator++(int)是唯一的两个有效签名,因此您无法对参数类型进行有意义的模板化。

答案 1 :(得分:1)

C ++内置协方差适用于C ++对象模型中的引用和指针。

现在这是C ++。因此,如果您不喜欢C ++提供的内容,您可以编写自己的对象模型。

在你的情况下你有迭代器。这些迭代器希望成为值类型(因为这是C ++在其库中想要的),并且您希望它们是多态的。

使用C ++对象模型不支持多态值类型。

使用code like thisadobe polyboost type erasureboost any_range,您可以创建支持值类型多态的鸭型多态系统。

现在,您的BaseClassB<T>::beginend会返回any_iterator<T>。符合概念的内容(包括DerivedClassA<T>)可以在其中存储和操作。 BaseClassA<T>变得过时,因为删除迭代器的类型不需要多态的虚基类。

DerivedClassB<T>也会返回any_iterator<T>。如果你想要对DerivedClassB<T>的真实迭代器进行裸访问有一个名为get_naked_range()的函数,它返回DerivedClassB<T>的裸迭代器,它可以在你绝对确定类型是的上下文中使用。 DerivedClassB<T>。如果您这样做,也请将beginend标记为final

请注意,此类型擦除具有运行时成本,并且迭代它将比“原始裸”迭代慢。这只有在高性能环境中以相当低的水平执行此操作时才重要,不要让它吓跑你。