为什么大多数 STL 实现中的代码如此复杂?
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);
}
请注意,我并不是在批评该界面,因为它设计得非常好并且适用。我关心的是实现细节的可读性。
之前曾提出过类似的问题:
注意:上面提供的代码取自 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
关于变量名称,库实现者必须使用“疯狂”的命名约定,例如以下划线开头的名称,后跟大写字母,因为这样的名称是为它们保留的。它们不能使用“正常”名称,因为这些名称可能已由用户宏重新定义。
第 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:
(Note that these rules forbid header guards like
__MY_FILE_H
which I have seen quite often.)Neil Butterworth,现在被列为“匿名”,在他对 SO 问题的回答中提供了一个有用的链接“是否有 STL 的可读实现?”。引用他的回答:
另请参阅该线程中的其他答案。
无论如何,大多数 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:
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.
变量名称,因为这是标准库代码,并且应该使用标头中实现细节的保留名称。以下内容不应破坏标准库:
因此标准库标头不能使用
mid
作为变量名称,因此不能使用_Mid
。 STL 是不同的 - 它不是语言规范的一部分,它被定义为“这里有一些标头,您可以随意使用它们”另一方面,如果您的代码或我的代码使用
将是无效的_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:
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: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.]实施情况各不相同。例如 libc++ 就更容易看懂。但仍然有一些下划线噪音。正如其他人所指出的,不幸的是需要前导下划线。 libc++ 中的相同函数如下:
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++:
我怀疑部分原因是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.
补充一下人们已经说过的,你看到的风格是 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.