在C ++中,标准库包含在std
命名空间中,程序员不应该在该命名空间内定义任何内容。当然,标准包含的文件不会在标准库中的每个其他名称上踩(因此包含标准头文件从来都不是问题)。
那么为什么默认情况下不包含整个标准库而不是强迫程序员每次都编写#include <vector>
?这将也加速编译,因为编译器可以从所有标准头文件的预构建符号表开始。
预先包含所有内容也可以解决一些可移植性问题:例如,当您包含<map>
时,它定义了哪些符号被带入std
命名空间,但它不能保证其他标准符号不加载到其中,例如,您可能最终(理论上)std::vector
也可以使用。
有时会发生程序员忘记包含标准标题,但程序仍会编译,因为包含了特定实现的依赖性。将程序移动到另一个环境(或只是同一编译器的另一个版本)时,相同的源代码可能会停止编译。
从技术角度来看,我可以对编译器进行映像,只需预加载(mmap
)标准库的最佳完美哈希符号表。
这应该比加载和执行甚至单个标准包含文件的C ++解析更快,并且应该能够更快地查找std::
名称。这些数据也是只读的(因此可能允许更紧凑的表示,并且可以在编译器的多个实例之间共享)。
然而,这些只是应该,因为我从未实现过这个。
我看到的唯一缺点是我们的C ++程序员会丢失编译咖啡休息和Stack Overflow访问: - )
为了澄清主要优势,我看到今天的程序员,尽管C ++标准库是一个单一的命名空间,但需要知道哪个子部分(包含文件)包含哪个函数/类。如果他们犯了错误并忘记了一个包含文件,那么代码可能会编译或者根据实现而无法编译(因此会导致非便携式程序)。
答案 0 :(得分:18)
简短回答是因为它不是应该使用C ++语言的方式
有充分的理由:
using namespace std;
。但是用using namespace std;
包括整个图书馆肯定会导致一团糟...... 就你的意思而言,Windows API就是一个例子,你只有一个大的include(windows.h)可以加载许多其他较小的包含文件。实际上,预编译头文件允许足够快
所以恕我直言,一种源自C ++的新语言可以决定自动声明整个标准库。一个新的主要版本也可以做到这一点,但它可能会使用using namespace
指令集中破解代码,并使用与某些标准模块相同的名称进行自定义实现。
但是我所知道的所有常用语言(C#,Python,Java,Ruby)都需要程序员声明他想要使用的标准库的部分,所以我想系统地提供标准库的每一部分仍然比程序员真正有用的更尴尬,至少在有人发现如何声明不应该加载的部分之前 - 这就是为什么我谈到了C ++的新衍生产品
答案 1 :(得分:9)
大多数C ++标准库都是基于模板的,这意味着它们生成的代码最终将取决于您如何使用它们。换句话说,在实例化std::vector<MyType> m_collection;
之类的模板之前,可以编译的内容很少。
此外,C ++可能是编译速度最慢的语言,当#include
包含其他标题的头文件时,编译器必须执行大量的解析工作。
答案 2 :(得分:6)
嗯,首先,C ++试图坚持“你只需支付你使用的费用”
标准库有时不是您使用的部分,甚至是您可以使用的部分。
此外,如果有理由,可以替换它:请参阅libstdc++
和libc++
。
这意味着毫无疑问地将它包括在内并不是一个明智的想法。
无论如何,委员会正在慢慢地创建一个模块系统(它需要很多时间,希望它适用于C ++ 1z:C++ Modules - why were they removed from C++0x? Will they be back later on?),并且当这样做时,最多的缺点包括更多标准库比严格必要的标准库应该消失,各个模块应该更清晰地排除它们不需要包含的符号。
此外,由于这些模块是预先解析的,因此它们应该提供您想要的编译速度提升。
答案 3 :(得分:5)
您的方案有两个优点:
编译时性能。但是标准中没有任何内容阻止实现通过非常轻微的修改来执行您建议的[*]:预编译表仅在转换单元包括至少一个标准头时映射。从标准的POV来看,没有必要对QoI问题施加潜在的实施负担。
方便程序员:根据您的方案,我们不必指定我们需要的标头。我们这样做是为了支持那些选择了 not 来实现你的标准头文件整体(当前就是所有这些)的想法的C ++实现,所以从C ++标准的POV来看它是一个“支持现有的做法和实施自由,代价是被认为可接受的程序员”。这是C ++的口号,不是吗?
由于没有C ++实现(我知道)实际上是这样做的,我的怀疑是,事实上它不授予您认为它的性能改进。出于性能原因,Microsoft提供了预编译头文件(通过stdafx.h
),但仍然没有为“所有标准库”提供选项,而是需要您说你想要的是什么。这个或任何其他实现很容易提供一个特定于实现的头,它定义为包含所有标准头的效果相同。这向我表明,至少在微软看来,提供这一点并没有太大的整体效益。
如果实现开始提供具有可证明的编译时性能改进的单片标准库,那么我们将讨论C ++标准是否继续允许不实现的实现是一个好主意。事实上,它必须。
[*]可能除了<cassert>
根据NDEBUG
的定义被定义为具有不同行为的事实。但我认为实现可以正常预处理用户的代码,然后根据它是否定义在两个不同的表中映射。
答案 4 :(得分:3)
我认为答案归结为C ++的理念,即不让你付出你不使用的东西。它还为您提供了更大的灵活性:如果您不需要,您不会被迫使用标准库的某些部分。然后有一些事实是某些平台可能不支持抛出异常或动态分配内存(例如Arduino中使用的处理器)。而且你说的另一件事是不正确的。只要它不是模板类,就可以将swap
运算符添加到您自己的类的std
命名空间中。
答案 5 :(得分:3)
首先,我担心有一个前奏对比赛有点晚。或者更确切地说,看作前奏并不容易扩展,我们必须满足于一个非常薄的(内置类型......)。
作为一个例子,让我们说我有一个C ++ 03程序:
#include <boost/unordered_map.hpp>
using namespace std;
using boost::unordered_map;
static unordered_map<int, string> const Symbols = ...;
一切正常,但是当我迁移到C ++ 11时突然发生了:
error: ambiguous symbol "unordered_map", do you mean:
- std::unordered_map
- boost::unordered_map
恭喜,您发明了用于发展标准库的最不向后兼容的方案(只是开玩笑,使用using namespace std;
的人应该受到指责......)。
好吧,让我们不要预先包含它们,但仍然捆绑完美的哈希表。性能提升是值得的,对吗?
嗯,我真的很怀疑。首先,因为与您包含的大多数其他头文件相比,标准库很小(提示:将其与Boost进行比较)。因此,性能提升会很小。
哦,并非所有节目都很大;但是那些小编译器已经快速编译(由于它很小)而且大编译器包含的代码比标准库标题要多得多,所以你不会从中获得太多的里程数。
注意:是的,我确实对项目中的文件查找进行了基准测试,仅使用&#34;&#34;一百个-I
指令;结论是预先计算&#34;包括路径&#34;到&#34;文件位置&#34;映射并将其提供给gcc导致加速30%(在使用ccache之后)。生成它并使其保持最新是很复杂的,所以我们从未使用它......
但是,我们至少可以包含编译器可以在标准中执行此操作的规定吗?
据我所知,已经包括在内。我不记得是否有一个特定的模糊,但标准库实际上是&#34;实现的一部分&#34;因此,将#include <vector>
解析为内部哈希映射无论如何都会属于as-if规则。
但他们仍然可以做到!
失去任何灵活性。例如,Clang可以在Linux上使用libstdc ++或libc ++,我相信它与VC ++附带的Dirkumware衍生产品兼容(或者如果不完全,至少非常重要)。
这是另一个定制点:如果标准库不适合您的需求,或者您的平台,由于主要被视为任何其他库,您可以相对轻松地替换大部分库。
但是!但是!
#include <stdafx.h>
如果您在Windows上工作,您将会识别它。这称为预编译头。它必须必须首先包含(或者所有的好处都会丢失)并且作为交换,而不是解析您正在提取这些已解析文件的有效二进制表示的文件(即,序列化的AST版本,可能具有某种类型已经执行的决议)可以节省30%到50%的工作量。是的,这接近你的建议;这对你来说就像是计算机科学,总是有人首先想到它......
Clang和gcc有类似的机制;虽然从我所听到的情况来看,使用它会让人感到非常痛苦,但人们更喜欢在实践中使用更透明的ccache。
所有这些都将通过模块来实现。
这是对此预处理/解析/类型解析疯狂的真正解决方案。因为模块是真正隔离的(即,与头部不同,不受包含顺序限制),可以为您依赖的每个模块预先计算的有效二进制表示(如预编译头)。
这不仅意味着标准库,而且所有库。
你的解决方案,更灵活,穿着九分!
答案 6 :(得分:2)
可以将C ++标准库的替代实现用于编译器附带的实现。或者使用一个定义包装标头,以添加,启用或禁用功能(请参阅GNU包装器标头)。纯文本标题和C包含模型是比二进制黑盒更强大,更灵活的机制。