Как обеспечить stl как контейнер с общедоступным итератором константы и частным итератором неконстанты?

У меня есть класс, который включает станд.:: список и желание предоставить общественности начинаются () и конец () для const_iterator, и частный начинаются () и конец () для просто итератора.

Однако компилятор видит частную версию и жалуясь, что это является частным вместо того, чтобы использовать общедоступную версию константы.

Я понимаю, что C++ не перегрузится на типе возврата (в этом случае const_iterator и итератор), и таким образом он выбирает версию неконстанты, так как мой объект не является константой.

За исключением кастинга моего объекта к константе перед вызовом начинаются (), или не перегрузка имени начинаются, там способ выполнить это?

Я думал бы, что это - известный шаблон, который люди решили прежде и хотели бы следовать примеру относительно того, как это обычно решается.

class myObject {
public:
  void doSomethingConst() const;
};

class myContainer {
public:
  typedef std::list<myObject>::const_iterator const_iterator;
private:
  typedef std::list<myObject>::iterator iterator;

public:
  const_iterator begin() const { return _data.begin(); }
  const_iterator end()   const { return _data.end();   }
  void reorder();
private:
  iterator begin() { return _data.begin(); }
  iterator end()   { return _data.end();   }
private:
  std::list<myObject> _data;
};

void myFunction(myContainer &container) {
  myContainer::const_iterator itr = container.begin();
  myContainer::const_iterator endItr = container.end();
  for (; itr != endItr; ++itr) {
    const myObject &item = *itr;
    item.doSomethingConst();
  }
  container.reorder(); // Do something non-const on container itself.
}

Ошибка из компилятора - что-то вроде этого:

../../src/example.h:447: error: `std::_List_iterator<myObject> myContainer::begin()' is private
caller.cpp:2393: error: within this context
../../src/example.h:450: error: `std::_List_iterator<myObject> myContainer::end()' is private
caller.cpp:2394: error: within this context

Спасибо.

- William

7
задан WilliamKF 13 March 2010 в 19:31
поделиться

4 ответа

Я думаю, ваш единственный вариант - переименовать частные методы (если они вам вообще нужны).

Кроме того, я считаю, что вам следует переименовать typedefs:

class MyContainer
{
public:
     typedef std::list<Object>::const_iterator iterator;
     typedef iterator const_iterator;

     const_iterator begin() const;
     const_iterator end() const;

private:
     typedef std::list<Object>::iterator _iterator;
     _iterator _begin();
     _iterator _end();
     ...
};

Контейнеры должны использовать typedef как итератор , так и const_iterator . Универсальная функция, принимающая неконстантный экземпляр вашего контейнера, может рассчитывать на использование итератора итератора typedef - даже если она не собирается изменять элементы. (Например, BOOST_FOREACH .)

Это будет нормально с точки зрения корректности констант, потому что если общая функция действительно попытается изменить объекты, реальный тип итератора (являющийся const_iterator ) не позволил бы.

В качестве теста следующий должен скомпилироваться с вашим контейнером:

int main()
{
    myContainer m;
    BOOST_FOREACH(const myObject& o, m) 
    {}
}

Обратите внимание, что m не является константой, но мы только пытаемся получить константные ссылки на содержащиеся типы, поэтому это должно быть разрешено .

4
ответ дан 6 December 2019 в 15:20
поделиться

Плохая идея наследовать от std :: list (он не предназначен для наследования).

Используйте переменную-член типа std :: list.

class myContainer
{
  std::list<myObject>   m_data;
  public:

    typedef std::list<myObject>::const_iterator myContainer::const_iterator;
  private:
    typedef std::list<myObject>::iterator myContainer::iterator;

  public:

    myContainer::const_iterator begin() const
    {
      return m_data.begin();
    }

    myContainer::const_iterator end() const 
    {
      return m_data.end();
    }

  private:
    myContainer::iterator begin()
    {
      return m_data.begin();
    }

    myContainer::iterator end()
    {
      return m_data.end();
    }
};
5
ответ дан 6 December 2019 в 15:20
поделиться

Вам необходимо изменить имя частного начального конца. Компилятор не может различать только возвращаемый тип

Это работает для меня: обратите внимание на имена _begin _end

#include <list>


class myObject {};

class myContainer : private std::list<myObject> {
public:
    typedef std::list<myObject>::const_iterator const_iterator;
private:
    typedef std::list<myObject>::iterator iterator;

public:
  myContainer::const_iterator begin() const {
    return std::list<myObject>::begin();
  }
  myContainer::const_iterator end() const {
    return std::list<myObject>::end();
  }
private:
  myContainer::iterator _begin() {
    return std::list<myObject>::begin();
  }
  myContainer::iterator _end() {
    return std::list<myObject>::end();
  }
};

void myFunction(myContainer &container) {
  myContainer::const_iterator aItr = container.begin();
  myContainer::const_iterator aEndItr = container.end();
  for (; aItr != aEndItr; ++aItr) {
    const myObject &item = *aItr;
    // Do something const on container's contents.
  }
}

int main(){
    myContainer m;
    myFunction(m);
}
4
ответ дан 6 December 2019 в 15:20
поделиться

Возможно, вы захотите изменить сигнатуру вашего метода Myfunction на такую:

void myFunction(const myContainer &container) 

потому что метод const будет вызываться только на объекте const. В настоящее время происходит то, что вы пытаетесь вызвать не const-метод, который в вашем случае является приватным.

1
ответ дан 6 December 2019 в 15:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: