为什么来自重写类的指针使用operator ==和operator!= from base class

时间:2014-11-18 13:51:47

标签: c++ pointers c++11 visual-studio-2013 iterator

我有用于for语句的类和类的Iterator模板。

template<class T>
class Itr2 {
public:
     Itr2()  { }
    ~Itr2()  { }

    typedef typename Itr2 type;
    typedef typename T& reference;

    virtual type& operator++()                      { return *this; }
    virtual T&    operator*()                       { return ((reference)*((type*)this)); }
    virtual bool  operator==(const type& o) const { return true; }
    virtual bool  operator!=(const type& o) const { return false; }
};


template<class T>
class I2
{
public:
    typedef I2<T> type;
    typedef T     value;
    typedef T& reference;
    typedef typename Itr2<T> iterator;

    virtual iterator& begin() { return *(new iterator()); }
    virtual iterator& end()   { return *(new iterator()); }
};

接下来,我为标准std :: vector&lt;&gt;创建了类。

template<class T>
class ItrSTD : public Itr2<T> {
public:
    typedef typename Itr2<T> base_type;
    typedef typename ItrSTD<T> type;
    typedef typename T& reference;
    typedef typename std::vector<T>::iterator std_itr;
protected:
    std_itr itr_;
public:
    ItrSTD(const type& o)                               { itr_ = o.itr_; }
    ItrSTD(const std_itr& o)                            { itr_ = o; }

    virtual base_type&  operator++()                    { itr_++; return *this; }

    virtual T&          operator*()                     { return ((reference)(*this->itr_)); }
    bool  operator==(const base_type& o) const override { return (((const type&)o).itr_ == this->itr_); }
    bool  operator!=(const base_type& o) const override { return (((const type&)o).itr_ != this->itr_); }
};



template<class T>
class VSTD : public I2<T> {
protected:
    std::vector<T>  arr_;
public:
    typedef typename ItrSTD<T>  iterator;

    VSTD(const VSTD& o)  { arr_ = o.arr_; }
    template<typename ...E> VSTD(E&&...e) : arr_({ std::forward<T>(e)... }) {  }

    iterator& begin()  _NOEXCEPT  override{ return (*new iterator(arr_.begin())); }
    iterator& end()    _NOEXCEPT  override{ return (*new iterator(arr_.end())); }

};

如果我使用直接语句(int i:v)。它工作正常,但是当我尝试从指针编译器执行此操作时,使用基类运算符!=(不覆盖运算符!=)并且代码不起作用:(。

int v_i = 0;
VSTD<int> vstd_a = { 1, 2, 3 };
I2<int> *i2 = &vstd_a;

for (int j : *i2) //DOESN't work :( use operator!= from base class
{
v_i += j;
}
for (int j : vstd_a) //work fine :)  use operator!= from VSTD.
{
 v_i += j;
}

如果我简化代码:

template<typename T>
class I3
{
public:
    T i;
    virtual bool  operator==(const I3& o) const { return false; }
};

template<typename T>
class I3O : public I3<T>
{
public:
    virtual bool  operator==(const I3& o) const override { return true; }
};

I3O<int> i3_a, i3_b; I3<int> *i3_ap, *i3_bp;

i3_ap = &i3_a; i3_bp = &i3_b; bool i3_c;

i3_c = (i3_a == i3_b);
i3_c = ((*i3_ap) == (*i3_bp));

两个结果都很好(返回true)并从override类进行比较(仅适用于(:)work bad :(:

为什么会这样。可以使用for语句作为模板指针吗? begin(),end()函数工作正常。只有操作员工作不同。

P.S。我使用VS2013编译器。

2 个答案:

答案 0 :(得分:4)

我没有查看代码中的详细信息,但是......看起来像 你试图使迭代器具有多态性。标准 然而,不断地通过它们传递它们,并且通过值并不是 支持多态性;切片。因此(我假设),在:

for ( int j : *i2 )

编译器为迭代器创建局部变量,其类型为 确定静态。虽然它调用了正确的begin(), 它将结果分配给此局部变量,从而对其进行切片。

如果需要多态迭代器,则需要实现 信/信封成语:

class Iterator
{
    Iterator* myImpl;

    virtual Iterator* clone() const { abort(); }
    virtual T& current() const { abort(); }
    virtual bool isEqual( Iterator const* other ) { abort(); }
    virtual void next() { abort(); }

protected:
    Iterator() : myImpl( nullptr ) {}
public:
    Iterator( Iterator* impl ) : myImpl( impl ) {}
    Iterator( Iterator const& other ) : myImpl( other.clone() ) {}
    virtual ~Iterator() { delete myImpl; }

    T& operator*() const
    {
        return myImpl->current();
    }
    bool operator==( Iterator const& other ) const
    {
        return myImpl->isEqual( other.myImpl );
    }
    Iterator& operator++()
    {
        myImpl->next();
        return *this;
    }
    //  ...
};

class DerivedIterator : public Iterator
{
    Iterator* clone() const override { return new DerivedIterator( *this );  }
    T& current() const override { ... }
    bool isEqual( Iterator const* other ) override { ... }
    virtual void next() { ... }
public:
    DerivedIterator(...) ...
};

然后,您的派生beginend会返回如下内容:

Iterator( new DerivedIterator(...) );

这在运行时相当昂贵,但它确实是唯一的方法 提供多态性并且仍然具有所需的值语义 迭代器。

答案 1 :(得分:1)

标准定义[stmt.ranged] / 1中基于范围的for语句的语义:

  

对于表单

的基于范围的for语句
for ( for-range-declaration : expression ) statement
     

range-init 等同于括号所包围的表达式

( expression )
     

以及表单

的基于范围的for语句
for ( for-range-declaration : braced-init-list ) statement
     

range-init 等同于 braced-init-list 。在每种情况下,基于范围的for语句等同于

{
  auto && __range = range-init;
  for ( auto __begin = begin-expr,
             __end = end-expr;
        __begin != __end;
        ++__begin ) {
    for-range-declaration = *__begin;
    statement
  }
}
     

其中__range__begin__end是仅为展示定义的变量,_RangeT是表达式的类型, begin-expr < / em>和 end-expr 确定如下:

     

(1.1) - 如果_RangeT是数组类型,...

     

(1.2) - 如果_RangeT是类类型,则在类begin的范围内查找不合格的ids end_RangeT,就像按类一样成员访问查找(3.4.5),如果其中任何一个(或两者)找到至少一个声明, begin-expr end-expr __range.begin(),分别为__range.end();

     

(1.3) - 否则,......

程序中的第一个循环:

for (int j : *i2) //DOESN't work :( use operator!= from base class
{
v_i += j;
}

因此等同于:

{
  auto && __range = (*i2);
  for ( auto __begin = __range.begin(),
             __end = __range.end();
        __begin != __end;
        ++__begin ) {
    int j = *__begin;
    v_i += j;
  }
}

由于I2<int>::beginI2<int>::end的返回类型为Itr2<int>&__begin__end将被推断为类型Itr2<int>,并且根据{{​​1}}和I2<int>::begin()的返回值构建。当然,该复制结构会对返回的类型进行切片,最终会得到基类型的实例。