2014-08-30 5 views
0

Я пытаюсь создать интерфейс Matrix. Этот класс будет реализован двумя классами. Один из них называется RegMatrix (Regular Matrix). Теперь я пытаюсь создать итераторы для классов RegMatrix и OthMatrix, и пусть пользователь сможет перебирать объект «Матрица». Проблема в том, что я получаю сообщение об ошибке «неверный тип возвращаемого значения для виртуального RegMatrix :: iterator RegMatrix :: begin()» для методов begin() и end(), вероятно, потому что один из них возвращает RegMatrix :: iterator, а базовый возвращает Matrix :: iterator. Я не понимаю, что на самом деле не так. Кто-нибудь знает, как исправить эту проблему? Спасибо.Ошибка «неверный код возвращаемого типа» в вложенных итераторах и inteface

Редактировать: Я понимаю, что из ваших текущих ответов мой дизайн недействителен. Итак, может ли кто-нибудь предложить лучший дизайн/решение для моей проблемы? итерация по «Матрице», которая может быть либо вложением «RegMatrix» (реализована с помощью карты для хранения данных), либо экземпляром «OthMatrix» (реализована с помощью Vector). эти два имеют разные итераторы, и я хочу, чтобы итератор Wrapper обертывал эти два, так что экземпляр будет прозрачным для пользователя при итерации. Благодарю.

класса Matrix (интерфейс):

class Matrix 
{ 
public: 
    class iterator 
    { 
    public: 
     virtual iterator& operator=(const iterator &other); 
     virtual ~iterator(){} 
     double operator*() const; 
     bool operator==(const iterator &other) const; 
     bool operator!=(const iterator &other) const; 
     iterator &operator++(); 
     iterator &operator++(int); 
    }; 

    virtual iterator begin() = 0; //*** ERROR : overriding 'virtual Matrix::iterator Matrix::begin()' *** 
    virtual iterator end() = 0; //*** ERROR : overriding 'virtual Matrix::iterator Matrix::end()' *** 

}; 

Класс Regular матрица:

class RegMatrix : public Matrix 
{ 
public: 
    RegMatrix() {//TODO }; 

    class iterator : public Matrix::iterator{ 

      friend class RegMatrix; 

     public: 

     iterator& operator=(const iterator &other) { 
      //TODO 
     } 

     ~iterator() {} 

     double operator*() const { 
      //TODO 
     } 


     bool operator==(const iterator &other) const { 
      //TODO 
     } 

     bool operator!=(const iterator &other) const { 
      //TODO 
     } 

     iterator &operator++() { 
      //TODO 
     } 

     iterator &operator++(int) 
     { 
      //TODO 
     } 

     iterator(Vector2D::iterator place) 
     { 
      rowIter = place; 
     } 
     private: 
      Vector2D::iterator rowIter; 
      Vector::iterator colIter; 

     }; 

     iterator begin() { //*** ERROR : invalid covariant return type for 'virtual RegMatrix::iterator RegMatrix::begin()' *** // 
     return iterator(matrix.begin()); 
     } 

     iterator end() { //*** ERROR : invalid covariant return type for 'virtual RegMatrix::iterator RegMatrix::end()' *** // 
      return iterator(matrix.end()); 
     } 

private: 
    Vector2D matrix; 
}; 
+0

Чтобы решить синтаксическую ошибку, вы не можете вернуть указатель или ссылку. Но вы можете видеть, есть еще одна проблема. Я думаю, вы должны пересмотреть свой дизайн. – wuqiang

+0

Я понимаю. так, может ли кто-нибудь предложить лучший дизайн/решение для моей проблемы? итерация по «Матрице», которая может быть либо вложением «RegMatrix» (реализована с помощью карты для хранения данных), либо экземпляром «OthMatrix» (реализована с помощью Vector). эти два имеют разные итераторы, и я хочу, чтобы итератор Wrapper обертывал эти два, так что экземпляр будет прозрачным для пользователя при итерации. Благодарю. – user2630165

+0

См. Мой отредактированный ответ для альтернативного подхода к дизайну. – Horstling

ответ

0

Ковариантные типы возвращаемых значений разрешается только при возврате указателя или ссылки (для основания и производного класса, соответственно,) - не при возврате по значению.

Подумайте об этом - вызывающий абонент должен знать, сколько памяти зарезервировано для хранения возвращаемого значения. Но sizeof(RegMatrix::iterator) > sizeof(Matrix::iterator). Как вызывающий абонент имеет только Matrix* указатель в руке должен знать, какие из них begin() звонок вернется?

1

Ваш код недействителен, потому что вы пытаетесь переопределить Matrix::iterator Matrix::begin() с помощью RegMatrix::iterator RegMatrix::begin(); типы возврата разные. Это разрешено только, если возвращаемые типы «ковариантны»:

§ 10.3.7

Возвращаемый тип доминирующей функции должен быть идентичен типа возвращаемого переопределенной функции или covariant с классами функций . Если функция D :: F отменяет функцию B :: F, типы возврата функций ковариантны, если они удовлетворяют следующих критериев:

  • как указатели на классы, и является Lvalue ссылки на классы , или оба являются значениями rvalue для классов
  • класс в обратном типе B :: f является тем же классом, что и класс в возвращаемом типе D :: f, или является однозначным и доступным прямым или косвенной базой класс класса в возвращаемом типе D :: f
  • оба указателя или ссылки имеют одинаковую cv-квалификацию и тип класса в возвращаемом типе D :: f имеет такую ​​же cv-квалификацию, что и или менее cv-qualification, чем тип класса в возвращаемом типе B :: f.

Поскольку ваши возвращаемые типы не являются ни указателей, ни ссылки, они не могут быть ковариантными.

Вы можете использовать идиом Pimpl, чтобы исправить свой дизайн; он позволяет иметь один тип итератора, который может выполнять итерацию по различным типам контейнеров, введя конкретную реализацию.

Вот разобранный пример, чтобы показать основную концепцию:

#include <memory> 

class Matrix 
{ 
public: 
    class iterator 
    { 
    public: 
     // this is the interface for the implementation the iterator is using 
     struct impl 
     { 
      virtual ~impl() {} 
      virtual double dereference() const = 0; 
      // [...] 
     }; 

     // the constructor takes a implementation 
     iterator(std::unique_ptr<impl> impl) 
      : m_impl{std::move(impl)} 
     { 
     } 

     double operator*() const 
     { 
      // all calls are referred to the concrete implementation 
      return m_impl->dereference(); 
     } 

     // [...] 

    private: 
     std::unique_ptr<impl> m_impl; 
    }; 

    virtual iterator begin() = 0; 
    virtual iterator end() = 0; 
}; 

class RegMatrix : public Matrix 
{ 
    // every Matrix has its own iterator implementation 
    class iterator_impl : public Matrix::iterator::impl 
    { 
    public: 
     iterator_impl(Vector2D::iterator itr) 
      : m_itr{itr} 
     { 
     } 

     virtual double dereference() const override 
     { 
      return *m_itr; 
     } 
     // [...] 

    private: 
     Vector2D::iterator m_itr; 
    }; 

    virtual iterator begin() override 
    { 
     // return a iterator that holds our iterator implementation 
     return iterator(std::unique_ptr<iterator_impl>{new iterator_impl{matrix.begin()}}); 
    } 

    virtual iterator end() override 
    { 
     return iterator(std::unique_ptr<iterator_impl>{new iterator_impl{matrix.end()}}); 
    } 

private: 
    Vector2D matrix; 
}; 

Это должно быть в состоянии решить вашу проблему, но она также имеет свои недостатки: Каждая конструкция итератора вызывает выделение динамической памяти, и все операции итератора приводит к вызову виртуального метода. Это может стать проблемой производительности в критических ситуациях, особенно потому, что итераторы предназначены для создания легких объектов.

Смежные вопросы