在今天的 Boost 库会议上, 《现代 C++ 设计和 Loki C++ 库》一书的作者Andrei Alexandrescu发表了题为“Iterators Must Go”(视频、幻灯片)的演讲,讲述了为什么迭代器不好,他有一个更好的解决方案。
我试图阅读演示幻灯片,但我无法从中得到太多。
- 迭代器不好吗?
- 他的替补真的更好吗?
- C++ 实现者会接受他的想法吗?
在今天的 Boost 库会议上, 《现代 C++ 设计和 Loki C++ 库》一书的作者Andrei Alexandrescu发表了题为“Iterators Must Go”(视频、幻灯片)的演讲,讲述了为什么迭代器不好,他有一个更好的解决方案。
我试图阅读演示幻灯片,但我无法从中得到太多。
首先,回答您的问题:
Andrei 在这里的重大贡献只是说:完全放弃迭代器的概念,将范围不仅视为一种方便的包装器,而且是一种核心构造。其他语言已经做到了这一点(Andrei 的大部分概念只是呼应 .NET 的 LINQ 或 Python 的迭代器),但它们都只提供输出范围。Andrei 主张不同类型的范围,就像传统的迭代器类别一样。
从这个角度来看,他开始嘲笑这些迭代器类别的任意性是很奇怪的。
我也认为他的例子已经过时了,尤其是他的文件复制:是的,迭代器变体是对 1975 年代码的巨大改进。它将具有复杂中断条件的循环减少为一条语句。他在这里真正遇到的问题只是语法。好吧,对不起:我们在这里谈论的是 C++——当然语法很丑。是的,在这里使用范围是一种改进——但只是在语法上。
我也认为 Andrei 的find
实施已经结束。他真正定义的是DropUntil
来自 LINQ 的操作(命名很难!)。该find
操作实际上应该返回一个或零个元素(或迭代器!)。在我看来,在这里避开迭代器没有帮助,因为我们可能想直接修改值而不是复制它。在这里返回一个元素范围只会增加开销而没有好处。这样做安德烈的方式是不好的,因为方法的名称是错误的和误导性的。
也就是说,我几乎在所有方面都同意安德烈。迭代器虽然是我在计算机科学中的宠物概念,但肯定是一个很大的语法负担,许多范围(尤其是无限生成器)可以(并且应该)在没有它们的情况下方便地实现。
我同意他的观点,迭代器大多不如范围,我不知道“更好的东西”是否会被采纳。
“好是最好的敌人”在这里非常重要,就像往常一样。迭代器很有用而且根深蒂固,所以很难知道像范围这样更好的东西是否可以在合理的时间内取代它们。
安德烈有时会有点挑衅。迭代器是一个合理的概念,并且在位的意义上是非常基础的。但是就像 C++ 中的大多数位不是布尔值,而是较大类型的一部分,大多数迭代器应该在高级别的地方处理。Andrei 是正确的,这样做的适当级别是范围对象。但并非所有范围都正确公开为迭代器范围,如 istream_iterator 哨兵所示。这只是创建人工结束迭代器的技巧。不过,我认为他的想法不会被实现所采纳。C++1x 将与 C99 一样重要。
C++0x 已经迈出了第一步:
在不丢失任何迭代器功能的情况下过渡到范围(想想迭代器类别、常量和右值的所有组合)是很困难的,尤其是当您尝试考虑无限和可变范围时。
不,它们还不错,实际上它们是非常聪明的主意。但是,它们并不理想,迭代器的概念还有改进的空间。
它用迭代器解决了许多现实生活中的问题。例如,在许多情况下,从单个容器中查询两个单独的对象(迭代器)然后将它们作为两个单独的对象传递给算法是很乏味的(也容易出错)。为什么不传递单个对象?甚至std::pair<iterator, iterator>
会产生一个更容易操作的粗略范围——一个对象,而不是两个。另外,考虑一下也是个好主意a range is an iterator
。这实际上是安德烈所建议的。顺便说一句, Boost.Range已经解决了其中一些问题。
我希望它会发生,但这不会是一场革命,而是进化。
我认为我们应该在迭代器旁边使用范围,即我们应该选择进化方式,而不是革命方式。
Andrei 不是在尝试为 D 语言做一些隐藏的营销(目前他正在使用它)......?
Andrei 说容器没问题,但迭代器丑陋、不直观、容易出错且危险,难以实现(最后一个似乎相当真实......)我们在 C++ 中有什么......指针? 它们不丑/.../危险吗?但我们很高兴地拥抱了他们并和他们一起生活。
哪一个写起来更直观:
for(auto i=foo.begin();i!=foo.end();++i)
bar(*i);
或者
for (auto r=foo.all(); !foo.empty(); foo.popFront())
bar(r.front());
迭代器的概念可以用范围和其他想法来补充,但我认为它们有自己的位置,不会被取代。
像任何 API 或函数一样,如果滥用会产生许多难以识别的问题。迭代器已在许多项目中使用,但始终根据其特性保持必要的注意。在使用它之前应该充分了解它们的局限性。如果用户正确,迭代器会非常有用。
这个问题是相关的:
有没有办法检查迭代器是否有效?
我应该更喜欢迭代器而不是 const_iterators 吗?
我不同意安德烈和康拉德以及我自己:-)
最基本的概念是接口而不是迭代器,这在今天任何人所做的任何工作中都很明显(这都是关于跨库、跨语言、跨编译器、跨操作系统、跨平台,你可以跨名字它 :-)
迭代器或范围(除了源代码级别的使用)都提供了一个干净简单、非侵入性或侵入性、非共享或共享、非唯一或唯一的:指针!指向类型数据的干净指针简单地说是通用的,您可以使数据可变或不可变以及许多其他事情。所有接口只是对它的另一个间接级别,同时仍然对各种机器和编译器友好,而且更安全,将迭代器和范围使用归为实现细节。
在那个程度上 IEnumerable 和 IQueryable 做了一半“正确的事情” TM,但它们的迭代概念显然不如你可以用 STL 做的事情,保留控制等等(但是,他们有更好的元数据因此是一个更好、更清洁的模型)。有了接口,您可以构建您想要并满足的任何抽象,很可能是相反的,但本质上是不费吹灰之力的:最佳的、运行时或编译时中性的数据表示和代码(对于算法、编译器和虚拟机来说是必不可少的) .
甚至可以针对“动态”/组件系统将其优化到“运行时”内联(screw HotSpot VM:-).. 在这个程度上,到 1975 年的进展是微乎其微的,因为巨大的互操作行业工作量很明显(它无处不在你看,包括这个网站,它对专有和开放技术的使用等;在计算机科学理想主义中,如果它不应该存在这种类型的接口“工作”)..
我认为 C++ 实现者将全力以赴为 C++0x 提供完整的工作支持,而无需实现新的非标准范式。
我可以从该演示文稿中看到的唯一论点是无法定义范围,而 c++0x“声明范围”提案似乎在某种程度上消除了这个问题。也许它不应该是关于是否应该/不应该使用迭代器的争论,但更多的是应该/不应该在什么情况下使用它们?