为什么大多数STL实现中的代码如此复杂?

STL是C ++世界的重要组成部分,大多数实现都是由Stepanov和Musser最初的努力所衍生出来的。

我的问题是代码的关键性,它是人们查看编写良好的C ++的例子,敬畏和学习的主要来源之一:为什么STL的各种实现如此令人厌恶地看着 – 令人费解和一般很好的例子,从美学的angular度来看如何不写C ++代码。

下面的代码示例不会在我工作的地方通过代码审查,因为variables命名,布局,macros和使用操作符的原因不尽相同,因为这些操作符需要的不仅仅是简单的一瞥,而是要查明实际发生的事情。

template<class _BidIt> inline bool _Next_permutation(_BidIt _First, _BidIt _Last) { // permute and test for pure ascending, using operator< _BidIt _Next = _Last; if (_First == _Last || _First == --_Next) return (false); for (; ; ) { // find rightmost element smaller than successor _BidIt _Next1 = _Next; if (_DEBUG_LT(*--_Next, *_Next1)) { // swap with rightmost element that's smaller, flip suffix _BidIt _Mid = _Last; for (; !_DEBUG_LT(*_Next, *--_Mid); ) ; _STD iter_swap(_Next, _Mid); _STD reverse(_Next1, _Last); return (true); } if (_Next == _First) { // pure descending, flip all _STD reverse(_First, _Last); return (false); } } } _Ty operator()() { // return next value static _Ty _Zero = 0; // to quiet diagnostics _Ty _Divisor = (_Ty)_Mx; _Prev = _Mx ? ((_Ity)_Ax * _Prev + (_Ty)_Cx) % _Divisor : ((_Ity)_Ax * _Prev + (_Ty)_Cx); if (_Prev < _Zero) _Prev += (_Ty)_Mx; return (_Prev); } 

请注意,我不批评接口,因为它devise得非常好,适用性也很好。 我所关心的是实现细节的可读性。

以前也提出类似的问题:

是否有可读的STL实现?

为什么STL实现是如此不可读? C ++如何在这里得到改进?

注意:上面提供的代码取自MSVC 2010algorithm和队列标题。

现在被列为“anon”的Neil Butterworth提供了一个有用的链接,他回答了SO问题“STL有可读的实现吗? 。 在那里引用他的回答:

有一本书“C ++标准模板库”,由原始STLdevise师Stepanov&Lee(与PJ Plauger和David Musser一起)共同撰写,描述了一个可能的实现,并附有代码 – 参见http://www.amazon。; co.uk/C-Standard-Template-Library/dp/0134376331 。

另请参阅该主题中的其他答案。

无论如何,大部分的STL代码(这里的STL代表的是类似于STL的C ++标准库的子集)是模板代码,因此必须是仅标题的,并且因为几乎在每个程序中都使用它,代码越短越好。

因此,简洁性和可读性之间的自然权衡点比简单性代码简洁得多。

此外,标准库是应用程序代码与系统无关的视图连接到底层系统的地方,利用了作为应用程序开发人员应该尽量远离的各种特定于编译器的事物。

干杯&hth。,

关于variables名称,库实现者必须使用“疯狂的”命名约定,例如名称以下划线开头,后跟大写字母,因为这些名字是为他们保留的。 他们不能使用“普通”名称,因为这些名称可能已被用户macros重新定义。

第17.6.3.3.2节“全球名称”§1指出:

某些名称和函数签名集总是保留给实现:

  • 每个包含双下划线的名称或以下划线开头并带有大写字母的名称都保留给实施用于任何用途。

  • 每个以下划线开头的名字都保留给实现,用作全局名称空间中的名称。

(请注意,这些规则禁止像__MY_FILE_H这样的标题保护,我经常看到这一点。)

因为这是标准库代码的原因,variables名称,它应该使用保留的名称作为头中的实现细节。 以下不应该打破标准库:

 #define mid #include <algorithm> 

所以标准库头文件不能使用mid作为variables名,因此_Mid 。 STL是不同的 – 它不是语言规范的一部分,它被定义为“这是一些标题,按照你的意思使用它们”

另一方面,如果你的代码或者我的代码使用了_Mid作为variables名,那么它将是无效的,因为这是一个保留的名字 – 允许执行:

 #define _Mid 

如果感觉像。

布局 – 嗯。 他们可能有一个风格指南,他们可能会跟着它,或多或less。 它不符合我的风格指南(因此会导致我的代码审查失败)的事实对他们来说没有任何意义。

难以运作的运营商 – 难以为之? 代码应该是为维护它的人编写的,而GNU / Dinkumware /谁可能不希望让那些不能解开标准库的人放弃*--_Next一个一目了然。 如果你用这种expression方式,你会习惯的,如果你不这样做,你会继续难以发现。

但是,我会给你, operator()超载是乱码。 [编辑:我明白了,它是一个线性同余生成器,非常一般地完成,如果模数为“0”,意味着只使用算术types的自然环绕。

实现有所不同。 例如, libc ++在眼睛上要容易得多。 尽pipe如此,还是有一些下划线的噪音。 正如其他人所指出的,不幸的是需要领先的下划线。 这是libc ++中的相同函数:

 template <class _Compare, class _BidirectionalIterator> bool __next_permutation(_BidirectionalIterator __first, _BidirectionalIterator __last, _Compare __comp) { _BidirectionalIterator __i = __last; if (__first == __last || __first == --__i) return false; while (true) { _BidirectionalIterator __ip1 = __i; if (__comp(*--__i, *__ip1)) { _BidirectionalIterator __j = __last; while (!__comp(*__i, *--__j)) ; swap(*__i, *__j); _STD::reverse(__ip1, __last); return true; } if (__i == __first) { _STD::reverse(__first, __last); return false; } } } 

我怀疑部分原因是STL中的代码是高度优化的。 正在实施的代码的性能比可读性更重要。 因为它们被广泛使用,所以尽可能快地使它们变得有意义。

为了增加人们已经说过的话,你看到的风格是GNU风格。 丑陋? 也许,这是在旁观者的眼中。 但它是一个严格定义的风格,它确实使所有代码看起来相似,而不是习惯于抵抗。