Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I avoid using a const_cast with std::vector::erase() in C++?

I have a class like this:

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

Apparently, std::vector::erase() cannot take a const_iterator, but std::find() requires one. I could cast away the const-ness of the iterator returned by std::find() when feeding it to std::vector::erase(), but Effective C++ taught me to regard const_cast with suspicion.

Is there another way to do this? I can't believe that something as common as removing an element from a vector should require type gymnastics. :)

like image 608
Josh Glover Avatar asked Dec 12 '22 13:12

Josh Glover


1 Answers

I suggest you change or overload your _iterator_for_node() function to accept a non-const reference to the list. The reason std::find returns a const_iterator is because the list itself is const, and therefore begin() and end() return const_iterators.

As an aside, const_cast<> won't actually convert a const_iterator to an iterator as the 'const' is just part of the name, not a CV-qualifier.

Also, you're not technically supposed to prefix names with an underscore, as this is reserved for implementations. (it will generally work in practice)

like image 186
pmdj Avatar answered Mar 06 '23 18:03

pmdj