【问题标题】:Convert object of custom template based iterator class to const_iterator将基于自定义模板的迭代器类的对象转换为 const_iterator
【发布时间】:2013-03-26 20:00:02
【问题描述】:

我正在大学学习 OOP 课程(C++ 是基础语言)。我的任务是实现自己的链表模板容器类。我几乎完全做到了,但遇到了问题。众所周知,STL 提供了iteratorconst_iterator 类用于遍历列表。它们的实现几乎相同,主要区别在于迭代器的方法返回引用,而 const_iterator 的方法——常量引用。我关注https://stackoverflow.com/a/3582733/2108548 并创建了单独的模板类ListIterator。然后我在List 类中声明了typedefIteratorConstIterator

我得到了这样的东西:

template<typename T>
class ListNode
{
public:
    ListNode(T *node_value = nullptr, ListNode *node_prev = nullptr, ListNode *node_next = nullptr):
        value(node_value), prev(node_prev), next(node_next) { }

    T *value;
    ListNode *prev, *next;
};

template<typename T>
class ListIterator
{
    typedef ListNode<T> Node;

public:
    ListIterator();
    ListIterator(Node *node);
    ListIterator(ListIterator const &other);
    ListIterator &operator++();
// ...
    Node *i;
};

template<typename T>
class List: public Container
{
    typedef ListIterator<T> Iterator;
    typedef ListIterator<T const> ConstIterator;
// ...
    Iterator begin() const
    {
        return Iterator(m_first->next);
    }
    ConstIterator const_begin() const
    {
        return ConstIterator(begin());
    }
// ...
};

在我决定制作“复制构造函数”Iterator -> ConstIterator 之前,一切都很好。所以我需要构造函数方法来获取ListIterator&lt;T&gt;(其中T 是数据类名称)并创建新的对象类型ListIterator&lt;T const&gt;。但实际上ConstIterator的构造函数获取T const作为模板参数,所以我需要去掉const作为构造函数的参数。我找到了标题type_traits,它可以做到这一点。所以我写了“复制构造函数”: typedef typename std::remove_cv::type NoConstT; ListIterator(ListIterator const &other);

但它不起作用!请求 const_begin() 后出现此错误:

List<int> list1;
list1 << 1 << 2 << 3;
int i = *list1.const_begin(); 

error: 'ListIterator<T>::ListIterator(const ListIterator<typename std::remove_cv<_Tp>::type>&) [with T = int; typename std::remove_cv<_Tp>::type = int]' cannot be overloaded with 'ListIterator<T>::ListIterator(const ListIterator<T>&) [with T = int; ListIterator<T> = ListIterator<int>]'

但这还不是全部。为了实现我的目标,还必须将ListNode&lt;T&gt; 转换为ListNode&lt;T const&gt;。但是我还有一个问题:每个列表节点都包含指向前一个和下一个节点的指针,如果我尝试在节点的构造函数中初始化它们,我将得到递归。当然,我可以创建函数,通过迭代将所有ListNode&lt;T&gt; 节点转换为ListNode&lt;T const&gt;。但我不喜欢这种解决方案:它的开销很大!

我问了我的老师这个问题。他愣了几分钟没看懂,等他明白了才说:“初级!” ——“但我坚持了 3-4 个小时!” — “如果是这样,请丢弃 const 迭代器并在没有它们的情况下完成列表容器。我需要时间来理解您的代码”(正如您所见,我的代码在我看来非常简单)。据我了解,他不知道这个问题的答案。但我真的很想知道怎么做!我该如何解决这个问题?

抱歉,有很多错误——我的母语不是英语。

【问题讨论】:

  • 您能否也添加造成这些错误的主要内容?
  • 您是否了解过如何处理 STL 类型?例如,我的实现 std::vector::iterator 实际上是 std::vector::const_iterator 的特化。
  • @Alon 更新错误描述
  • 在我将其更改为 const 后,就像我的答案一样,它对我有用
  • 好的,我添加了一个解决方案。我下班后会再次访问它

标签: c++ templates copy-constructor const-iterator listiterator


【解决方案1】:

您确实可以使用&lt;type_traits&gt;,只是不像您描述的那样。一种方法是始终声明相同类型的构造函数,并仅在模板参数确实不是常量时使用enable_if 有条件地从非常量声明一个构造函数。并且节点应该总是非常量,这你可以用remove_const来做。

#include <type_traits>

template<typename T>
class ListNode
{
   //...   
};

template<typename T>
class ListIterator
{
    typedef ListNode<typename std::remove_const<T>::type> Node;

public:
    ListIterator() {}
    ListIterator(Node*) {}
    ListIterator(ListIterator const&) {}
    template <typename U>
    ListIterator(ListIterator<U> const&, typename std::enable_if<!std::is_const<U>()>::type* = nullptr) {}
};

template<typename T>
class List
{
public:
    typedef ListIterator<T> Iterator;
    typedef ListIterator<T const> ConstIterator;
// ...
    Iterator begin()
    {
        return Iterator(/*...*/);
    }
    ConstIterator const_begin() const
    {
        return ConstIterator(/*...*/);
    }
// ...
};

int main() {
    List<int> list;
    List<int>::ConstIterator ci = list.const_begin(); 
    List<int>::Iterator i = list.begin(); 
    ci = i; // works fine as expected
    i = ci; // fails as expected
    i = i;  // works fine as expected
    ci = ci;  // works fine as expected
    return 0;
}

【讨论】:

  • 非常感谢!它完美地工作!有了这个解决方案,就不需要方法 const_begin。也不错。
  • 这太棒了,我知道必须有一种更好的方法来表达可变和不可变的迭代器类型,而不是在整个类/时间连续体中传播模板元样板。
猜你喜欢
  • 2020-01-03
  • 1970-01-01
  • 1970-01-01
  • 2015-07-09
  • 1970-01-01
  • 2021-11-01
  • 2014-01-09
  • 2021-10-22
相关资源
最近更新 更多