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

发布于 2024-10-02 14:25:16 字数 1775 浏览 7 评论 0原文

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

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

之前曾提出过类似的问题:

是否有一个可读的实现STL的

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

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

The STL is a critical piece of the C++ world, most implementations derive from the initial efforts by Stepanov and Musser.

My question is given the criticality of the code, and it being one of the primary sources for people to view examples of well written C++ for both awe and learning purposes: Why are the various implementations of the STL so disgusting to look at - convoluted and generally good examples of how not to write C++ code from an aesthetics point of view.

The code examples below would not pass code-review at the places I've worked at for reasons varying from variable naming, layout, macros and uses of operators that require more than a simple glance to figure out what is actually occurring.

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

Please note I'm not critiquing the interface, as it is very well designed and applicable. What I'm concerned about is the readability of the implementation details.

A similar questions have been previously posed:

Is there a readable implementation of the STL

Why STL implementation is so unreadable? How C++ could have been improved here?

Note: The code presented above is taken from MSVC 2010 algorithm and queue headers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

偏闹i 2024-10-09 14:25:16

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

第 17.6.3.3.2 节“全局名称”§1 规定:

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

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

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

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

About the variables names, library implementors must use "crazy" naming conventions, such as names starting with an underscore followed by an uppercase letter, because such names are reserved for them. They cannot use "normal" names, because those may have been redefined by a user macro.

Section 17.6.3.3.2 "Global names" §1 states:

Certain sets of names and function signatures are always reserved to the implementation:

  • Each name that contains a double underscore or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.

  • Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.

(Note that these rules forbid header guards like __MY_FILE_H which I have seen quite often.)

无可置疑 2024-10-09 14:25:16

Neil Butterworth,现在被列为“匿名”,在他对 SO 问题的回答中提供了一个有用的链接“是否有 STL 的可读实现?”。引用他的回答:

有一本书《C++ 标准》
模板库,由
最初的STL设计师Stepanov &李
(与 PJ Plauger 和 David
Musser),它描述了一种可能的
实现,完整代码 -

http://www.amazon.co.uk/C-标准模板库/dp/0134376331

另请参阅该线程中的其他答案。

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

因此,简洁性和可读性之间的自然权衡点在简洁性方面比“正常”代码要远得多。

此外,标准库是将应用程序代码的系统独立视图连接到底层系统的地方,利用了作为应用程序开发人员最好远离的各种特定于编译器的东西。

Neil Butterworth, now listed as "anon", provided a useful link in his answer to the SO question "Is there a readable implementation of the STL?". Quoting his answer there:

There is a book The C++ Standard
Template Library, co-authored by the
original STL designers Stepanov & Lee
(together with P.J. Plauger and David
Musser), which describes a possible
implementation, complete with code -
see
http://www.amazon.co.uk/C-Standard-Template-Library/dp/0134376331.

See also the other answers in that thread.

Anyway, most of the STL code (by STL I here mean the STL-like subset of the C++ standard library) is template code, and as such must be header-only, and since it's used in almost every program it pays to have that code as short as possible.

Thus, the natural trade-off point between conciseness and readability is much farther over on the conciseness end of the scale than with "normal" code.

In addition, the standard library is where the system-independent view of application code is connected to the underlying system, utilizing all kinds of compiler-specific things that you as an application developer should best stay away from.

猛虎独行 2024-10-09 14:25:16

变量名称,因为这是标准库代码,并且应该使用标头中实现细节的保留名称。以下内容不应破坏标准库:

#define mid
#include <algorithm>

因此标准库标头不能使用 mid 作为变量名称,因此不能使用 _Mid。 STL 是不同的 - 它不是语言规范的一部分,它被定义为“这里有一些标头,您可以随意使用它们”

另一方面,如果您的代码或我的代码使用 将是无效的_Mid 作为变量名,因为这是一个保留名称 - 允许实现这样做:

#define _Mid

如果感觉像这样的话。

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

难以算出的算子——对谁来说很难?代码应该是为维护它的人编写的,GNU/Dinkumware/任何可能不想让人们在标准库上松懈的人,他们一眼就看不出*--_Next 。如果你使用这种表达方式,你就会习惯它,如果你不这样做,你就会继续觉得很难。

不过,我会告诉你,operator() 重载是胡言乱语。 [编辑:我明白了,它是一个线性同余生成器,非常通用,如果模数为“0”,则意味着仅使用算术类型的自然环绕。]

Variable names for the reason that this is standard library code, and it should use reserved names for implementation details in headers. The following should not break the standard libraries:

#define mid
#include <algorithm>

So standard library headers can't use mid as a variable name, hence _Mid. The STL was different - it wasn't part of the language specification, it was defined as "here are some headers, use them as you will"

Your code or mine, on the other hand, would be invalid if it used _Mid as a variable name since that's a reserved name - the implementation is allowed to do:

#define _Mid

if it feels like it.

Layout - meh. They probably have a style guide, they probably follow it, more or less. The fact that it doesn't match my style guide (and hence would fail my code review) is nothing to them.

Operators that are difficult to work out - difficult to whom? Code should be written for the people who maintain it, and GNU/Dinkumware/whoever probably don't want to let people loose on the standard libraries who can't puzzle out *--_Next at a glance. If you use that sort of expression, you get used to it, and if you don't you'll continue finding it hard.

I will give, you, though, that operator() overload is gibberish. [Edit: I get it, it's a linear congruential generator, done very generically, and if the modulus is "0" that means just use the natural wraparound of the arithmetic type.]

鸠魁 2024-10-09 14:25:16

实施情况各不相同。例如 libc++ 就更容易看懂。但仍然有一些下划线噪音。正如其他人所指出的,不幸的是需要前导下划线。 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;
        }
    }
}

Implementations vary. libc++ for example, is much easier on the eyes. There's still a bit of underscore noise though. As others have noted, the leading underscores are unfortunately required. Here's the same function in 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;
        }
    }
}
じ违心 2024-10-09 14:25:16

我怀疑部分原因是STL中的代码是高度优化的。正在实现的代码类型的性能比可读性更重要。由于它们的使用如此广泛,因此尽可能快地制造它们是有意义的。

I suspect part of the reason is that the code in the STL is highly optimized. The sort of code being implemented has performance being much more important then readability. Because they are so widely used it makes sense to make them as fast as possible.

万劫不复 2024-10-09 14:25:16

补充一下人们已经说过的,你看到的风格是 GNU 风格。丑陋的?或许,这就是情人眼里出西施。但它是一种严格定义的风格,它确实使所有代码看起来都很相似,而不是难以适应。

To add on what people have said already, the style you see is the GNU style. Ugly? Perhaps, that's in the eye of the beholder. But it's a strictly-defined style, and it does make all code look similar, as opposed to resistant to getting used to.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文