哪些标准c ++类不能在c ++中重新实现?

时间:2008-10-29 16:47:18

标签: c++ compiler-construction

我正在查看C ++ 0x的计划,并且std::initializer_list用于在用户类中实现初始化列表。该类无法在C ++中实现 没有使用自己,或者使用一些“编译魔术”。如果可以,则不需要它,因为您用于实现initializer_list的任何技术都可用于在您自己的类中实现初始化列表。

还有哪些类需要某种形式的“编译器魔术”才能工作?标准库中哪些类无法由第三方库实现?

编辑:也许应该说实例化而不是实现。事实上,这个类与语言功能直接相关(你不能使用没有initializer_list的初始化列表)。

与C#进行比较可能会清除我想知道的内容:IEnumerable和IDisposable实际上是硬编码成语言功能。我一直认为C ++是免费的,因为Stroustrup试图让所有东西都可以在库中实现。那么,是否有任何其他类/类型与语言特征密不可分。

8 个答案:

答案 0 :(得分:5)

我唯一能想到的是typeid返回的type_info类。据我所知,VC ++通过在编译时静态实例化所有需要的type_info类来实现这一点,然后根据vtable中的值在运行时简单地生成一个指针。这些可以使用C代码完成,但不能以符合标准或可移植的方式完成。

答案 1 :(得分:5)

std::type_info是一个简单的类,虽然填充它需要typeinfo:编译器构造。

同样,异常是普通对象,但抛出异常需要编译器魔术(分配的异常在哪里?)。

对我来说,问题是“如果没有编译魔术,我们能够接近std::initializer_list吗?”

查看wikipediastd::initializer_list<typename T>可以通过看起来很像数组文字的东西来初始化。让我们尝试给我们的std::initializer_list<typename T>一个转换构造函数,它接受一个数组(即一个带有T[]参数的构造函数):

namespace std {
     template<typename T> class initializer_list {
         T internal_array[];
         public:
         initializer_list(T other_array[]) : internal_array(other_array) { };

         // ... other methods needed to actually access internal_array
     }
}

同样,使用std::initializer_list的类通过声明一个带有单个std::initializer_list参数的构造函数来实现这一点 - a.k.a.转换构造函数:

struct my_class {
    ...
    my_class(std::initializer_list<int>) ...
}

所以这一行:

 my_class m = {1, 2, 3};

使编译器思考:“我需要调用my_class的构造函数; my_class有一个构造函数,它带有std::initializer_list<int>;我有一个int[]字面值;我可以将int[]转换为std::initializer_list<int>;我可以将其传递给my_class构造函数“(请先阅读答案的结尾,然后再告诉我C ++没有' t允许两个隐式的用户定义转换被链接)。

那有多接近?首先,我缺少初始化列表的一些功能/限制。我没有强制执行的一件事是初始化程序列表只能用数组文字构造,而我的initializer_list也会接受已经创建的数组:

int arry[] = {1, 2, 3};
my_class = arry;

此外,我没有理会使用右值参考。

最后,如果编译器隐式地将两个用户定义的转换链接在一起,则此类仅适用于新标准。这在正常情况下是特别禁止的,因此该示例仍然需要编译魔术。但我认为(1)类本身是一个普通类,(2)涉及的魔法(强制执行“数组文字”初始化语法并允许两个用户定义的转换被隐式链接)比它看起来要少乍一看。

答案 2 :(得分:1)

根据定义,标准库中的所有类都必须 才能在C ++中实现。其中一些隐藏了一些不起眼的语言/编译器结构,但仍然只是围绕这种复杂性的包装,而不是语言功能。

答案 3 :(得分:1)

在定义的点上运行时“挂钩”的任何东西很可能不能作为假设语言“C ++中的可移植库”实现,不包括那个东西。

例如,我认为&lt; cstdlib&gt;中的atexit();不能纯粹作为库实现,因为在C ++中没有其他方法可以确保在终止序列中的适当时间调用它,即在任何全局析构函数之前。

当然,你可以争辩说这个问题的C特征“不计算”。在这种情况下,std :: unexpected可能是一个更好的例子,原因完全相同。如果它不存在,那么在没有修改编译器发出的异常代码的情况下就没有办法实现它。

[编辑:我刚注意到提问者实际上询问了什么 classes 无法实现,而不是标准库的哪些部分无法实现。实际上这些例子并没有严格回答这个问题。]

答案 4 :(得分:1)

C ++允许编译器定义其他未定义的行为。这使得在非标准C ++中实现标准库成为可能。例如,“onebyone”对atexit()感到奇怪。库编写者可以假设编译器使得他们的非可移植C ++对他们的编译器工作正常。

答案 5 :(得分:1)

MSalter在评论中指出了printf / cout / stdout。您可以根据其他任何一个实现其中任何一个(我认为),但是如果没有OS调用或编译器魔法,您无法在一起实现它们的整个集合,因为:

  1. 这些是访问流程标准输出流的所有方法。你必须在某处填充字节,并且在没有这些东西的情况下这是特定于实现的。除非我忘记了另一种访问它的方法,但重点是你不能通过特定于实现的“魔法”来实现标准输出。

  2. 他们在运行时有“神奇”行为,我认为纯粹的库无法完全模仿它。例如,您不能仅使用静态初始化来构造cout,因为未定义编译单元之间的静态初始化顺序,因此无法保证它会及时存在以供其他静态初始化程序使用。 stdout可能更容易,因为它只是fd 1,所以支持它的任何设备都可以通过它们看到它时传入的调用来创建。

答案 6 :(得分:0)

我认为你在这方面非常安全。 C ++主要用作围绕C的厚层抽象。由于C ++ 是C本身的超集,因此核心语言原语几乎总是实现sans-classes(以C风格)。换句话说,你不会发现许多情况,比如Java Object,这是一个具有硬编码到编译器中的特殊含义的类。

答案 7 :(得分:0)

再次从C ++ 0x开始,我认为线程不能作为假设语言“C ++ 0x中的可移植库实现,除了线程之外还有所有标准库”。

[编辑:只是为了澄清,对于“实施线程”意味着什么似乎存在一些分歧。我在这个问题的背景下理解的是:

1)实现C ++ 0x线程规范(无论结果如何)。注意C ++ 0x,这是我和提问者都在谈论的内容。不是任何其他线程规范,例如POSIX。

2)没有“编译魔术”。这意味着不向编译器添加任何内容以帮助您的实现工作,也不依赖于任何非标准实现细节(例如特定的堆栈布局,或切换堆栈的方法,或非便携式系统调用来设置定时中断) )生成一个仅适用于特定C ++实现的线程库。换句话说:纯粹的,可移植的C ++。你可以使用信号和setjmp / longjmp,因为它们是便携式的,但我的印象还不够。

3)假设一个C ++ 0x编译器,除了它缺少C ++ 0x线程规范的所有部分。如果它缺少的是一些数据结构(存储退出值和join()或等效使用的同步原语),但是存在编译魔术来实现线程,那么显然可以将数据结构添加为第三方便携式组件。但是当问题是关于哪些C ++ 0x标准库类需要编译器魔术来支持它们时,这是一个枯燥的答案。 IMO。]