题
我在许多Python程序中都使用了yield,它 真的 在许多情况下清除代码。我 在博客上谈论了它 它是我网站的热门页面之一。
C#还提供了yield——它是通过调用方的状态保持来实现的,通过自动生成的类来完成,该类保存函数的状态、局部变量等。
我目前正在阅读有关 C++0x 及其补充的内容;在阅读 C++0x 中 lambda 的实现时,我发现它也是通过自动生成的类完成的,配备有存储 lambda 代码的operator()。我脑海中自然浮现出一个问题:他们为 lambda 做了这件事,为什么他们不考虑用它来支持“yield”呢?
他们当然可以看到协同例程的价值......所以我只能猜测他们认为基于宏的实现(例如 西蒙·泰瑟姆)作为适当的替代品。然而,由于多种原因,它们并非如此:被调用者保持状态、不可重入、基于宏(仅此一点就足够了)等。
编辑: yield
不依赖于垃圾收集、线程或纤程。你可以阅读Simon的文章,看到我说的是编译器做一个简单的转换,比如:
int fibonacci() {
int a = 0, b = 1;
while (true) {
yield a;
int c = a + b;
a = b;
b = c;
}
}
进入:
struct GeneratedFibonacci {
int state;
int a, b;
GeneratedFibonacci() : state (0), a (0), b (1) {}
int operator()() {
switch (state) {
case 0:
state = 1;
while (true) {
return a;
case 1:
int c = a + b;
a = b;
b = c;
}
}
}
}
垃圾收集?不。线程?不。纤维?不。简单改造?可以说,是的。
解决方案
他们为 lambda 这么做了,为什么他们不考虑用它来支持收益率呢?
检查 文件. 。有人提议吗?
...我只能猜测他们认为基于宏的实现是一个足够的替代品。
不必要。我确信他们知道存在这样的宏观解决方案,但替换它们本身并不足以推动新功能的通过。
尽管围绕新关键字存在各种问题,但这些问题可以通过新语法来克服,例如对 lambda 进行的操作以及使用 auto 作为函数返回类型。
全新的功能需要强大的驱动力(即人)充分分析并通过委员会推动功能,因为他们总是有很多人对彻底的改变持怀疑态度。因此,即使没有您认为反对收益率结构的强有力的技术原因,可能仍然没有足够的支持。
但从根本上来说,C++ 标准库采用了与您在yield 中看到的不同的迭代器概念。与 Python 的迭代器相比,它只需要两个操作:
- an_iter.next() 返回下一个项目或引发 StopIteration(next() 内置于 2.6 中,而不是使用方法)
- iter(an_iter) 返回 an_iter (因此您可以在函数中同等对待迭代器和迭代器)
C++ 的迭代器成对使用(必须是相同的类型),被分为不同的类别,这将是一种语义转变,转换为更适合yield 构造的东西,并且这种转变不太适合概念(它有此后已被删除,但来得相对较晚)。例如,请参阅 理由 for (合理地,如果令人失望地)拒绝我关于将基于范围的 for 循环更改为一种形式的评论,这种形式将使编写这种不同形式的迭代器变得更加容易。
具体澄清我对不同迭代器形式的含义:您生成的代码示例需要 其他 type 是迭代器类型加上用于获取和维护这些迭代器的相关机制。并不是说它无法处理,而是它并不像您一开始想象的那么简单。真正的复杂性是“简单转换”,尊重“局部”变量的异常(包括在构造期间),控制生成器内局部范围内“局部”变量的生命周期(大多数需要在调用之间保存),等等。
其他提示
我不能说他们为什么不添加这样的东西,但就 lambda 而言,他们没有 只是 也添加到语言中。
他们最初是在 Boost 中实现库,这证明了
- lambda 用途广泛:当它们可用时,很多人都会使用它们,并且
- C++03 中的库实现存在许多缺点。
基于此,委员会决定通过 某种 C++0x 中的 lambda,我相信他们最初尝试添加更多通用语言功能以允许 更好的 库实现比 Boost 还要多。
最终,他们将其作为核心语言功能,因为他们别无选择:因为不可能制作一个 够好了 库实施。
新的核心语言功能并不是简单地添加到语言中,因为它们看起来是个好主意。该委员会是 非常 不愿意添加它们以及有问题的功能 真的 需要证明自己。必须证明该特征是:
- 可以在编译器中实现,
- 去解决真正的需求,并且
- 库的实现还不够好。
在这种情况下,如果 yield
关键字,我们知道第一点就可以解决。正如您所展示的,这是一个相当简单的转换,可以机械地完成。
第二点很棘手。多少钱一个 需要 因为这个有吗?现有的库实现的使用范围有多大?有多少人提出过这个要求,或者为此提交了提案?
最后一点似乎也通过了。至少在 C++03 中,库实现存在一些缺陷,正如您所指出的, 可以 证明核心语言实现的合理性。那么 C++0x 中是否可以实现更好的库实现呢?
所以我怀疑主要问题确实是缺乏兴趣。C++ 已经是一门庞大的语言,没有人希望它变得更大,除非添加的功能是 真的 值得。我怀疑这还不够有用。
添加的关键字总是棘手的,因为它使无效先前有效的代码。尽量避免,在用代码库一样大C ++语言。
C ++的演变是一个公共程序。如果你觉得yield
应该在那里,制定一个适当的请求,C ++标准委员会。
您会得到你的答案,直接从谁决定的人。
所以看起来它没有进入 C++11 或 C++14,但可能正在进入 C++17。来看看讲座吧 C++ 协程,负开销抽象 来自 CppCon2015 和论文 这里.
总而言之,他们正在努力扩展 C++ 函数,以将yield 和await 作为函数的功能。看起来他们在 Visual Studio 2015 中有一个初步实现,不确定 clang 是否有实现。此外,使用yield 和await 作为关键字似乎可能存在一些问题。
该演示很有趣,因为他谈到了它如何简化了网络代码,您正在等待数据进入以继续处理序列。令人惊讶的是,看起来使用这些新的协程会产生比现在更快/更少的代码。这是一个很棒的演示。
C++ 的可恢复函数建议可以找到 这里.
在一般情况下,你可以跟踪发生了什么事情由委员会文件,虽然它的用于跟踪,而不是寻找一个特定的问题更好。
有一点要记住关于C ++委员会是,它是一个志愿者委员会,并不能完成它想要的一切。例如,在原标准无散型地图,因为他们无法管理,使之及时。这可能是因为有对谁关心不够有关yield
和它确保了下班做了什么委员会没人管。
找出将询问有源委员的最佳方式。
那么,对于这样一个简单的例子为,我看到的唯一问题是,std::type_info::hash_code()
未指定constexpr
。我相信,一个符合标准的实现仍然可以做起来很支持这一点。但无论如何,真正的问题是获得唯一标识符,因此有可能是另一种解决方案。 (显然我借你的“主开关”构建体,谢谢。)
#define YIELD(X) do { \
constexpr size_t local_state = typeid([](){}).hash_code(); \
return (X); state = local_state; case local_state: ; } \
while (0)
用法:
struct GeneratedFibonacci {
size_t state;
int a, b;
GeneratedFibonacci() : state (0), a (0), b (1) {}
int operator()() {
switch (state) {
case 0:
while (true) {
YIELD( a );
int c = a + b;
a = b;
b = c;
}
}
}
}
嗯,他们还需要保证哈希不为0没什么大不了的有两种。和一个DONE
宏很容易实现。
真正的问题是,当你从本地对象范围返回会发生什么。有没有节省关在一个基于C语言的堆栈帧的希望。解决的办法是使用一个真正的协程,和C ++ 0X直接做地址与线程和期货。
考虑该发电机/协程:
void ReadWords() {
ifstream f( "input.txt" );
while ( f ) {
string s;
f >> s;
yield s;
}
}
如果一个相似的技巧用于yield
,f
在第一yield
破坏,是非法的继续后循环,因为你不能goto
或switch
过去非POD对象定义。
已有数协程执行作为用户空间库。然而,这里是交易,那些实现依赖于非标准细节。例如,无处在C ++标准指定的栈帧是如何保持。大多数实现刚才复制的堆栈,因为这是多大多C ++实现工作
关于标准,C ++可通过改进堆栈帧的说明书中已经帮助协程的支持。
其实“添加”它的语言听起来是不是好主意给我,因为那样会坚持你对于大多数情况下,“足够好”的实现,完全是编译器相关。对于其中使用协程事项的情况下,这是不能接受的反正