我在许多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 的迭代器相比,它只需要两个操作:

  1. an_iter.next() 返回下一个项目或引发 StopIteration(next() 内置于 2.6 中,而不是使用方法)
  2. 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;
    }
}

如果一个相似的技巧用于yieldf在第一yield破坏,是非法的继续后循环,因为你不能gotoswitch过去非POD对象定义。

已有数协程执行作为用户空间库。然而,这里是交易,那些实现依赖于非标准细节。例如,无处在C ++标准指定的栈帧是如何保持。大多数实现刚才复制的堆栈,因为这是多大多C ++实现工作

关于标准,C ++可通过改进堆栈帧的说明书中已经帮助协程的支持。

其实“添加”它的语言听起来是不是好主意给我,因为那样会坚持你对于大多数情况下,“足够好”的实现,完全是编译器相关。对于其中使用协程事项的情况下,这是不能接受的反正

首先同意@Potatoswatter 的观点。

支持协程与支持 lambda 不同,而不是 简单的变换 就像用达夫的设备玩一样。

你需要 完全不对称协程 (stackful)像 Python 中的生成器一样工作。实施 西蒙·泰瑟姆克里斯' 都是无堆栈的,而 Boost.协程 虽然很重,但还是满满一叠。

不幸的是,C++11仍然没有 yield 对于协程来说,也许是 C++1y ;)

附:如果你真的喜欢 Python 风格的生成器,请看一下 .

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top