2010-12-18 133 views
4

我仍然试图实现我自己版本的LinkedList类,现在我对常量迭代器的重载方法有问题。例如,当我尝试使用此代码打印出清单:C++:为常量迭代器重载list.end()和list.begin()方法

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

所以据我的理解,这意味着那些平常结束,并开始迭代方法被使用。下面是这些方法是如何在我的类中声明:

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

有什么办法,我可以强迫我的程序使用的常量迭代器,而不是常使用的这些常量的方法呢?我很乐意听到任何建议。

哦,这是我在一个文件类的代码,以防万一:http://pastebin.com/Jbvv5Hht

+0

您的代码看起来不错,我已经添加在引擎收录运营商有些“consts”。可能不会帮助,但代码看起来OK – Drakosha 2010-12-18 12:35:42

+0

谢谢你,但我仍然得到这些错误=( – Chris 2010-12-18 12:47:01

回答

7

您应该IterCIter提供一个转换。标准集装箱做(表65中,23.1“集装箱需求”,说X::iterator可转换为X::const_iterator

主叫方可以确保const过载使用const引用调用,但你不应该强迫他们做到这一点,因为他们将不得不写类似:

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

如果您提供的“必需的”转换那么就没有必要为您的来电者做什么特别的:你原来的代码将工作,只是因为它的标准容器。

+0

嗯,但我可以强制它使用的开始和结束返回CITER类型的方法,避免转换 – Chris 2010-12-18 12:23:55

+0

@克里斯:你意思是“我”,如“我的人写的容器”,或者“我”中一样。是后者,没有前者“编写使用容器代码的人。”你为什么要实现抗?在 – 2010-12-18 12:32:38

+0

,因为我真的不知道我该怎么实现转换我做了一个构造函数接收Iter项目和进口Iter项目链接到CITER,并设法使=并CITER标准中使用的容器接口=为CITER! - ITER的运营商,但我仍然得到同样的错误信息 – Chris 2010-12-18 12:46:32

0

你需要

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

这不是我的方法^^“ – Chris 2010-12-18 12:24:15

+0

但它应该是:这是编译器在第二个错误消息中抱怨的内容 – 2010-12-18 12:29:24

-2

它使用常规的方法开始,因为变量不是常量。所以固定它的一种方式是使另一个(参考)变量,常量:

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

或者,使用的const_cast。

+0

你不需要(并且不应该使用const_cast来添加const) – 2010-12-18 12:16:17

+0

当然,你应该使用const_cast来添加const,这就是整个观点,如果你使用static_cast,你可能会不合需要地执行错误的强制转换。是一个编译器检查,你执行的唯一转换是增加或删除const(或volatile) – 2010-12-18 12:19:10

+1

const_cast的“整点”是它是唯一可以删除const的类型,但是,如果有存在你的容器可以转换的其他类型,编译器检查你是不是转换成其中的一个在理论上是有用的。当然,正如你所说,使用const_cast可能会让我你在添加const的同时意外删除了volatile,这可能是主要的原因,尤其是*不能*使用const_cast来添加const。取决于你希望编译器能够捕捉哪个错误。 – 2010-12-18 12:23:08

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); 
//... 

您使用后缀运算符++的IT2,但没有实现你的迭代器类。Borland通过使用前缀来接受它,但是会给出警告。 Gcc实际上将其标记为错误并拒绝代码。可能要考虑这样做

+0

好的,谢谢:)我会改变我的代码,因为你建议 – Chris 2010-12-18 14:07:29

1

感叹:这里有可以隐藏在概念上你想要做什么,不能用C自动完成++,因为它不理解差异的事实两轮牛车的水平。其他一些语言(包括Ocaml)也可以。

如果你有一个函子(这是C++程序员的一个模板类),问题是它和各种函数如何随参数的变化而变化,比如从T到T const的转换。你真正想要的是:

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

换句话说,你希望列表仿函数是协变的。但不,实际上,List模板根本不是函子,因为函子必须是结构保留的,并且转换不会按照需要反映出来。这又意味着要么C++模板被破坏,要么const的概念被破坏,因为不支持参数多义的类型系统被指定破坏:)

提供“const_iterator”不能解决这个问题,它只是简单的修补休息。 volatile和const_volatile版本在哪里?双重间接如何?

如果你不明白双重间接性:考虑的T向量的树,这两个模板:

Tree<Vector<T>> 

这里最好的解决办法就是放弃支持常量性。只是不要打扰。无论如何,它是困惑的:“const vector”怎么样?那是什么?一个矢量你不能再延长,但它仍然允许你编写元素?

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

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

[或它们如果变换是禁忌变体抗通勤]

事实上不会发生这种情况表明,矢量不是功能性的,换句话说,模板不能有效地用于参数多态性。如果你想真正让你的内裤打结,可以考虑带有函数参数的模板,并询问函数的返回类型和参数的变化,以及这会如何影响容器。一个很好的例子是如何编写两个函数,以便他们在一对中工作。如果他们是增变器,那么“const”如何工作呢?

+0

嗯有趣 – Chris 2010-12-19 06:08:35