9

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

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

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

与 C# 的比较可能会弄清楚我想知道的问题:IEnumerable 和 IDisposable 实际上是硬编码到语言功能中的。我一直认为 C++ 没有这种情况,因为 Stroustrup 试图让所有东西都可以在库中实现。那么,是否有任何其他类/类型与语言功能密不可分。

4

8 回答 8

5

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

于 2008-10-29T16:47:53.130 回答
5

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

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

std::initializer_list对我来说,问题是“如果没有编译器魔法,我们离 s 有多近?”

查看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
     }
}

同样,使用 a 的类std::initializer_list通过声明一个接受单个std::initializer_list参数的构造函数来实现这一点——也就是转换构造函数:

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

所以这一行:

 my_class m = {1, 2, 3};

导致编译器认为:“我需要为 ; 调用一个构造函数my_classmy_class有一个接受 a 的构造函数std::initializer_list<int>;我有一个int[]文字;我可以将 an 转换int[]为 a std::initializer_list<int>;我可以将它传递给my_class构造函数”(请阅读到末尾在告诉我 C++ 不允许链接两个隐式用户定义转换之前的答案)。

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

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

此外,我没有打扰右值引用。

最后,这个类只有在编译器隐式地将两个用户定义的转换链接在一起时才能按照新标准所说的那样工作。这在正常情况下是特别禁止的,所以这个例子仍然需要编译器的魔法。但我认为(1)类本身是一个普通类,(2)所涉及的魔法(强制“数组文字”初始化语法并允许隐式链接两个用户定义的转换)比看起来要少乍一看。

于 2008-10-30T00:14:19.937 回答
1

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

于 2008-10-29T16:58:10.597 回答
1

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

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

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

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

于 2008-10-29T23:51:01.120 回答
1

C++ 允许编译器定义其他未定义的行为。这使得在非标准 C++ 中实现标准库成为可能。例如,“onebyone”想知道 atexit()。库编写者可以假设有关编译器的事情,使他们的非可移植 C++ 在他们的编译器上工作正常。

于 2008-10-30T10:03:49.637 回答
1

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

  1. 这些都是访问进程标准输出流的所有方式。您必须在某处填充字节,并且在没有这些东西的情况下这是特定于实现的。除非我忘记了另一种访问它的方式,但关键是除了通过特定于实现的“魔术”之外,您无法实现标准输出。

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

于 2008-10-30T15:38:13.160 回答
0

我认为你在这个分数上很安全。C++ 主要用作围绕 C 的一层厚厚的抽象层。由于 C++也是C 本身的超集,因此核心语言原语几乎总是实现无类(以 C 风格)。换句话说,你不会找到很多像 JavaObject这样的情况,Java 是一个具有特殊含义的类,硬编码到编译器中。

于 2008-10-29T17:06:39.487 回答
0

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

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

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

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

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

于 2008-10-29T23:41:43.760 回答