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

64 c++ stl readability

STL是C++世界的关键部分,大多数实现源自Stepanov和Musser的初步努力.

我的问题是代码的重要性,并且它是人们为敬畏和学习目的查看编写良好的C++示例的主要来源之一:为什么STL的各种实现如此令人作呕? - 复杂和一般来说,如何不从美学的角度来编写C++代码的好例子.

下面的代码示例不会在我工作过的地方通过代码审查,原因不同于变量命名,布局,宏和操作符的使用,这些操作符需要的不仅仅是简单的一瞥,以确定实际发生的情况.

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);
   }
Run Code Online (Sandbox Code Playgroud)

请注意我并不批评界面,因为它设计得很好并且适用.我关心的是实现细节的可读性.

之前提出过类似的问题:

是否有可读的STL实现

为什么STL实现如此难以理解?如何在这里改进C++?

注意:上面提供的代码取自MSVC 2010算法和队列标头.

Che*_*Alf 19

Neil Butterworth,现在被列为"anon",在他回答SO问题时提供了一个有用的链接:"STL是否有可读的实现?" .在那里引用他的答案:

有一本书C++标准模板库,由最初的STL设计师Stepanov&Lee(与PJ Plauger和David Musser一起)共同撰写,描述了一个可能的实现,包括代码 - 请参阅 http://www.amazon. co.uk/C-Standard-Template-Library/dp/0134376331.

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

无论如何,大多数STL代码(由STL我在这里指的是C++标准库的类似STL的子集)是模板代码,因此必须是仅头文件,并且因为它几乎在每个程序中都使用它所支付的代码尽可能短.

因此,简洁性和可读性之间的自然权衡点远远超过了"正常"代码的简洁性末端.

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

  • 一个很好的链接,我将把它添加到问题中.+1 (3认同)

fre*_*low 18

关于变量名称,库实现者必须使用"疯狂"命名约定,例如以下划线后跟大写字母开头的名称,因为这些名称是为它们保留的.它们不能使用"普通"名称,因为这些名称可能已由用户宏重新定义.

第17.6.3.3.2节"全球名称"§1规定:

某些名称和函数签名集始终保留给实现:

  • 每个包含双下划线或以下划线后跟大写字母开头的名称都保留给实现以供任何使用.

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

(请注意,这些规则禁止__MY_FILE_H我经常看到的标题保护.)

  • @peoro命名空间不保护您免受预处理器宏的影响.`_Names`是为实现保留的,所以如果你有这样的宏,它就在你的头上.另一方面,您不应该被迫猜测STL实现可能决定在内部使用不应该创建宏的标识符,特别是因为这可能因实现而异. (6认同)

Ste*_*sop 12

变量名称,因为这是标准库代码,它应该使用保留名称来实现标头中的实现细节.以下应打破标准库:

#define mid
#include <algorithm>
Run Code Online (Sandbox Code Playgroud)

因此,标准库头不能mid用作变量名_Mid.STL是不同的 - 它不是语言规范的一部分,它被定义为"这里有一些标题,按照你的意愿使用它们"

另一方面,如果您的代码或我的代码用作_Mid变量名,那么它将是无效的,因为这是一个保留名称 - 允许执行:

#define _Mid
Run Code Online (Sandbox Code Playgroud)

如果它感觉像.

布局 - 呃.他们可能有一个风格指南,他们可能会或多或少地遵循它.它与我的风格指南不符(因此我的代码审查失败)这一事实对他们来说并不算什么.

难以解决的运营商 - 难以为谁服务?代码应该是为维护代码的人编写的,而GNU/Dinkumware /可能不希望让人们忽略标准库而不能*--_Next一目了然.如果你使用那种表达方式,你会习惯它,如果你不习惯,你会继续努力找到它.

不过,我会说,operator()超载是胡言乱语.[编辑:我明白了,它是一个线性同余生成器,非常一般地完成,如果模数为"0",则意味着只使用算术类型的自然环绕.]

  • 但这确实打破了标准库,当然它会.当`first`变为空时,他们如何定义`std :: pair`? (2认同)
  • 啊,我看,所以它的某种反盗版/反规避技术...... :) (2认同)