Как я могу избежать использования const_cast с std :: vector :: erase () в C ++? - PullRequest
4 голосов
/ 20 апреля 2011

У меня есть такой класс:

  template<class T>
  class AdjacencyList {
  public:
    void delete_node(const T&);

  protected:
    const typename std::vector<T>::const_iterator _iterator_for_node(
        const std::vector<T>&, const T&
    );
  };

  template<class T>
  void AdjacencyList<T>::delete_node(const T& node) {
    _nodes.erase(_iterator_for_node(_nodes, node));
  }

  template<class T>
  const typename std::vector<T>::const_iterator AdjacencyList<T>::_iterator_for_node(
      const std::vector<T>& list, const T& node
  ) {
    typename std::vector<T>::const_iterator iter =
        std::find(list.begin(), list.end(), node);
    if (iter != list.end())
      return iter;

    throw NoSuchNodeException();
  }

Очевидно, std::vector::erase() не может взять const_iterator, но std::find() требует его.Я мог отбросить const -ность итератора, возвращенного std::find() при подаче его на std::vector::erase(), но Эффективный C ++ научил меня относиться к const_cast с подозрением.

Есть ли другой способ сделать это?Я не могу поверить, что что-то такое общее, как удаление элемента из вектора, должно требовать гимнастики типа.:)

Ответы [ 3 ]

7 голосов
/ 20 апреля 2011

Я предлагаю вам изменить или перегрузить функцию _iterator_for_node(), чтобы принять неконстантную ссылку на список.Причина, по которой std::find возвращает const_iterator, заключается в том, что сам список равен const, и, следовательно, begin() и end() возвращают const_iterator с.

В целом, const_cast<> выиграл 'на самом деле const_iterator преобразуется в iterator, поскольку const - это только часть имени, а не CV-квалификатор.

Кроме того, технически нельзя использовать префикс имени с подчеркиванием, так как это зарезервировано для реализаций.(как правило, это будет работать на практике)

5 голосов
/ 20 апреля 2011

Помимо моей прямой модификации кода , вот идея:

Вместо функции-члена _iterator_for_node, которая

  • имеет постоянные проблемы
  • бесполезно тесно связан с конкретным типом контейнера (вызывая беспорядок разрешения шаблона имени типа)
  • не делает ничего, кроме std :: find и выдает исключение, если не найдено

Я предлагаю вместо этого создать следующую статическую (глобальную / namespace) функцию:

template<class It, class T>
    It checked_find(It begin, It end, const T& node)
{
    It iter = std::find(begin, end, node);
    if (iter != end)
        return iter;

    throw NoSuchNodeException();
}

Он будет работать с любым типом итераторов (включая не-STL, итераторы входного потока, только вперед, const, обратные итераторы ... вы называете это) и не требует явного различия между const / неконстантные версии:)

С ним

рабочая версия вашего кода будет просто читать

template<class T>
class AdjacencyList {
        std::vector<T> _nodes;
    public:
        void delete_node(const T& node) 
        { _nodes.erase(checked_find(_nodes.begin(), _nodes.end(), node)); }
};

Обратите внимание на сокращение кода. Всегда хороший знак

Приветствия

1 голос
/ 20 апреля 2011

Кажется, что есть некоторая путаница между константными элементами и константными итераторами в вашем коде.

Не заглядывая в сценарий использования, я предлагаю следующее «исправление» для компиляции:

#include <algorithm>
#include <vector>
#include <iostream>

using namespace std;

struct NoSuchNodeException {};

template<class T>
class AdjacencyList {
        std::vector<T> _nodes;
    public:
        void delete_node(const T&);

    protected:
        typename std::vector<T>::iterator _iterator_for_node(std::vector<T>&, const T&);
        typename std::vector<T>::const_iterator _iterator_for_node(const std::vector<T>&, const T&) const;
};

template<class T>
void AdjacencyList<T>::delete_node(const T& node) {
    _nodes.erase(_iterator_for_node(_nodes, node));
}

template<class T>
    typename std::vector<T>::iterator AdjacencyList<T>::_iterator_for_node(std::vector<T>& list, const T& node)
{
    typename std::vector<T>::iterator iter = std::find(list.begin(), list.end(), node);
    if (iter != list.end())
        return iter;

    throw NoSuchNodeException();

}

template<class T>
    typename std::vector<T>::const_iterator AdjacencyList<T>::_iterator_for_node(const std::vector<T>& list, const T& node)  const
{
    typename std::vector<T>::const_iterator iter = std::find(list.begin(), list.end(), node);
    if (iter != list.end())
        return iter;

    throw NoSuchNodeException();
}

int main()
{
    AdjacencyList<int> test;
    test.delete_node(5);
}
...