C++11中的所有STL都是用户可实现的吗

Is everything in C++11 STL user-implementable?

本文关键字:用户 可实现 STL C++11      更新时间:2023-10-16

我知道像<map><vector><algorithm>这样的库可以由用户实现,即使它们在标准库中不存在。

但是,C++11STL中的一些类或函数只是"编译器魔法",无法手动实现吗?


EDIT我指的是不需要额外链接的标准模板库

如果你指的是标准库中源自Alexander Stepanov的标准模板库的部分,那么你可以在没有任何编译器"魔法"的情况下实现所有这些(即,你只需要C++标准的其余部分所需的正常功能)。

算法在迭代器上运行。他们需要从迭代器中进行的操作由他们操作的迭代器类定义(见下文)。该算法简单地执行正常操作,例如分配给元素和交换元素。

所有迭代器都提供一些操作,如增量和取消引用。双向迭代器还提供递减,随机访问迭代器提供加法/减法。这些都不是特别复杂——它使用标准编译器功能来实现。

容器与算法的相似之处在于,它们主要通过迭代器对数据进行操作。它们还使用分配器类来分配内存、在内存中创建对象等等。尽管编写分配器过去相当复杂(而且需求文档记录得很差),C++11已经大大简化了任务(而且它从不需要任何非标准的东西)。

如果你把"STL"理解为STandard库,那么是的,有相当多的部分需要各种各样的编译器魔法。

其中一些是根据较低级别的组件定义的,因此(例如)iostream是根据C的getchar/puthchar1来定义的,以从外部世界获取数据。其他部分(例如,type traits、std::uncaught_exception)为编译器生成的数据提供了接口,但通过其他方式无法用于可移植代码。


1。然而,请注意,即使iostream是根据C函数的读写来定义的,它们也不需要实际使用这些C函数,只是做与它们相同的事情。

我假设你指的是C++11标准库,而不是STL(它是20世纪90年代的算法、迭代器和容器库)。

答案取决于你所说的"手动执行"是什么意思。你是说纯C++吗?即没有汇编代码,也没有特定于操作系统的功能,如POSIX内存分配函数,甚至没有对内核的较低级别系统调用?

因为如果您准备直接将汇编和调用写入内核,那么您几乎可以实现所有内容,但这不是很实用。即使是编写自己的std::malloc()operator new,如果不将其构建在较低级别(如sbrk())之上,也是非常困难的。如果没有像malloc这样的东西,就很难编写std::allocator,也很难实现std::vector

std::system_clock还依赖于操作系统提供的设施,您需要一个较低级别的API,如POSIX的clock_gettime(),或者访问CPU中的硬件时钟。即使是<ctime>中定义的std::time()也需要类似的东西。

因此,让我们假设依赖C库来获得malloc等功能是可以的,但我们希望避免编写汇编和非C++。

在纯C++中不可能有效地实现CCD_ 15。您需要一些编译器魔术或汇编代码来提供必要的同步保证。std::atomic的libstdc++实现依赖于GCC的__atomic内置插件,这是"编译器魔术"。std::atomic的libc++实现使用了_Atomic关键字,该关键字由C11定义,并由clang++支持,但在标准C++中没有定义。在这两种情况下,库实现都依赖于编译器的非标准功能来提供提供所需行为所需的平台特定程序集。

或者,您可以使用互斥体低效地实现std::atomic,但这只会将对"神奇"同步属性的要求转移到互斥体类型上。至少,一个实现需要提供std::atomic_flag,这在纯C++中是做不到的。如果没有std::atomic_flag,其他原子类型和std::mutex就无法在纯C++中实现,因此需要特定于平台的汇编代码,或者需要使用提供必要原语的较低级别库,如Pthreads(它们本身将在汇编中实现,或者使用不可移植的编译器魔术)。

正如在How do std::async"存储";一个任意的例外?C++运行时的一些部分,如std::typeinfostd::exception_ptr,可以用C++编写,但不能移植。运行时必须定义RTTI和异常处理的数据结构和内部细节,并提供编译器将调用的入口点,以便C++代码中的throw从运行时调用相关例程来分配异常对象。所以你可以自己实现这些东西,但除非你符合正确的API,否则编译器不会使用它们,所以它们不会工作!

但是,即使您准备将汇编和调用直接写入内核,也有几个类型特征依赖于编译器的魔力,用户无法实现,例如is_standard_layoutis_trivialis_trivially_constructible和其他is_trivially_xxx特征。这些都取决于无法在代码中测试的类型的属性,只有编译器才能进行必要的检查,并告诉您类型是否具有该属性(这同样适用于C++14is_final特性)。

并非所有这些都是用户可实现的。某些类只能使用编译器扩展来实现。

GCC为类型特征提供了"编译器魔法"。

  • https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Type-Traits.html#Type-特性

GCC还提供原子内建,

  • https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/_005f_005fatomic-Builtins.html#_005f_005fatomic-建筑物

initializer_list通常是在编译器本身可以访问私有函数的情况下实现的,在这种情况下,该函数就是构造函数。

依赖于<typeinfo>标头的typeid运算符就是一个例子。

另一种情况是initializer_list,它基本上是一个具有特殊核心语言权限的库插件。例如,在变量初始化规则中会考虑它。

我想还有更多的东西是附加库和基本语言结构,但这些都是我现在能记住的。