如何避免在 C++ 中使用 const_cast 和 std::vector::erase() ?

发布于 2024-11-02 13:53:48 字数 1266 浏览 1 评论 0原文

我有一个这样的类:

  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() > 需要一个。当将 std::find() 返回的迭代器提供给 std::vector::erase()< 时,我可以抛弃它的 const-ness /code>,但是 Effective C++ 教会我尊重const_cast 带着怀疑。

还有其他方法可以做到这一点吗?我不敢相信像从向量中删除元素这样常见的事情竟然需要类型体操。 :)

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. :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

如若梦似彩虹 2024-11-09 13:53:48

我建议您更改或重载 _iterator_for_node() 函数以接受对列表的非常量引用。 std::find 返回 const_iterator 的原因是列表本身是 const,因此 begin()end() 返回 const_iterator

顺便说一句,const_cast<> 实际上不会将 const_iterator 转换为 iterator,因为“const”只是名称的一部分,不是 CV 限定符。

另外,从技术上讲,您不应该在名称前添加下划线,因为这是为实现保留的。 (实践中一般都会起作用)

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)

骄兵必败 2024-11-09 13:53:48

除了我的直接修改代码,这里有一个想法:

成员函数 _iterator_for_node

  • 不是将具有 const 问题的
  • 毫无用处地紧密绑定到特定容器类型(导致类型名模板解析混乱),
  • 什么也不做比 std::find and 如果找不到则抛出异常

我建议创建以下静态(全局/命名空间)函数:

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/非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)); }
};

注意代码的减少。永远是好兆头

干杯

Aside from my direct modification of the code, here's an idea:

Instead of a member function _iterator_for_node which

  • has const issues
  • is uselessly tightly bound to a particular container type (inducing a typename template resolution mess)
  • does nothing more than std::find and throw an exception if not found

I suggest creating the following static (global/namespace) function instead:

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

It will work with any iterator type (including non-STL, input stream iterators, forward only, const, reverse iterators... you name it) and it doesn't require explicit distinction between const/non const versions :)

With it,

a working version of your code sample would just read

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

Note the code reduction. Always the good sign

Cheers

提赋 2024-11-09 13:53:48

代码中的 const 元素和 const 迭代器之间似乎存在相当大的混淆。

在不查看用例的情况下,我建议使用以下“修复”来进行编译:

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

There seems to be quite some confusion between const elements, and const iterators in your code.

Without looking in to the use case, I propose the following 'fix' to make things compile:

#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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文