C ++:重载常量迭代器的list.end()和list.begin()方法

时间:2010-12-18 12:05:40

标签: c++ linked-list iterator overloading c++builder

我仍在尝试实现我自己的LinkedList类版本,现在我遇到了为常量迭代器重载方法的问题。例如,当我尝试使用以下代码打印出列表时:

cout << "citer:" << endl;
for (UberList<int>::CIter it = ulist.begin(); it != ulist.end(); ++it)
{
 cout << *it << " ";
}
cout << endl;

我有这些错误:

Error E2034 UberList2.cpp 532: Cannot convert 'UberList<int>::Iter' to 'UberList<int>::CIter' in function main()
Error E2094 UberList2.cpp 532: 'operator!=' not implemented in type 'UberList<int>::CIter' for arguments of type 'UberList<int>::Iter' in function main()

据我所知,这意味着使用那些通常的end和begin迭代器方法。以下是我的课程中声明这些方法的方法:

Iter begin();
Iter end();
CIter begin() const;
CIter end() const;

template<class T>
typename UberList<T>::Iter UberList<T>::begin()
{
    Iter it;
    it.curr = head;
    return it;
}

template<class T>
typename UberList<T>::Iter UberList<T>::end()
{
 Iter it;
 it.curr = tail->next;
 return it;
}

template<class T>
typename UberList<T>::CIter UberList<T>::begin() const
{
 CIter it;
 it.ccurr = head;
 return it;
}

template<class T>
typename UberList<T>::CIter UberList<T>::end() const
{
 CIter it;
 it.ccurr = tail->next;
 return it;
}

有什么方法可以强制我的程序将这些const方法用于常量迭代器而不是通常的迭代器?我很高兴听到任何建议。

哦,这是我的课程代码,以防万一:http://pastebin.com/Jbvv5Hht

5 个答案:

答案 0 :(得分:7)

您应提供从IterCIter的转化。标准容器(表65,在23.1“容器要求”中表示X::iterator可转换为X::const_iterator

调用者可以通过使用const引用确保调用const重载,但是你不应强迫他们这样做,因为他们必须编写如下内容:

UberList<int>::CIter it = static_cast<const UberList<int> &>(ulist).begin()

如果您提供“必需”转换,则您的调用者无需执行任何特殊操作:您的原始代码将起作用,就像对标准容器一样。

答案 1 :(得分:1)

对OP代码的更多评论。考虑分离这个巨大的uberl33tlist类并将其分解为更小的文件。看到所有这些朋友的课堂宣言让我感到很不舒服。当你使用像

这样的东西时,还有一些棘手的语义
friend class UberList;
friend class CIter;

在某些情况下,如果那些类尚不存在,那么这些语句最终也会向转发声明这些类。在这里还有一些关于你的任务操作员不太正确的事情:

UberList<T> operator = (const UberList<T>& OL)
{
    UberList<T> NL = new (OL);
    return NL;
}

另外在你的主要你有

it2++;
ulist.insertAfter(b, it2);
//...

你正在使用postfix operator ++ for it2但是没有实现你的迭代器类。 Borland通过使用前缀来接受它,但会发出警告。 Gcc实际上将其标记为错误并拒绝代码。可能想看看那个

答案 2 :(得分:1)

叹息:这里有一定程度的hackery旨在隐藏这样一个事实,即概念上你想要做的事情不能在C ++中自动完成,因为它不了解方差。其他一些语言(包括Ocaml)可以。

如果你有一个仿函数(这是C ++程序员的模板类),问题是它和各种函数如何用参数的方差表现,例如从T到T的转换。你真正想要的是:

List<T> --> List<T const>

换句话说,你希望List仿函数是协变的。但不,它不是..所以实际上List模板根本不是一个仿函数,因为仿函数必须是结构保留的,并且转换不会根据需要反映出来。反过来,这意味着C ++模板被破坏或者const的概念被破坏,因为不支持参数多态的类型系统被规范打破了:)

提供“const_iterator”并不能解决这个问题,只是简单地修补了这个问题。 volatile和const_volatile版本在哪里?双重间接怎么样?

如果你不理解双重间接:考虑T的向量树,这是两个模板:

Tree<Vector<T>>

这里最好的解决方案是放弃支持const_iterator。只是不要打扰。无论如何它都很混乱:“const vector”怎么样?那是什么?你不能再做一个矢量,但它仍然允许你写元素吗?

实际要求是变换通勤,例如:

vector<T> const == vector<T const>

[或者如果变换是反变的,他们反通勤]

事实并非如此,这表明矢量不是函数,换句话说,模板不能有效地用于参数多态。如果你想真正地将你的短裤绑在一个结上,请考虑带有函数参数的模板,并询问函数返回类型和参数的方差,以及这可能如何影响容器。一个很好的例子是如何组合两个函数,以便它们在一对上工作。如果它们是变异器,那么“const”如何工作呢?

答案 3 :(得分:0)

你需要

teamplate<class T> bool operator!=(UberList<T>::CIter,UberList<T>::CIter);

答案 4 :(得分:-2)

它使用常规begin方法,因为变量不是const。因此,修复它的一种方法是创建另一个(引用)变量const:

UberList<int> const & culist = ulist;
for (UberList<int>::Citer it = culist.begin(); ...)

或者,使用const_cast。