【问题标题】:Why can't VS2017 coroutines return void?为什么VS2017协程不能返回void?
【发布时间】:2017-08-31 21:54:54
【问题描述】:

我正在使用目前在 VS2017 中进行实验的建议的 c++ 协同例程进行一些实验。我只是想拥有一个不返回任何内容但在某个协程对象上调用 co_await 的协程,比方说,在恢复之前在另一个线程上进行一些处理。但是,即使是返回 void 的最基本的协程程序,VS 也不会编译。例如:

#include "stdafx.h"
#include <experimental\coroutine>

using namespace std::experimental;

void bob()
{
    co_await suspend_always{};
}

int main()
{
    bob();
}

导致错误:

1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.10.25017\include\experimental\resumable(46): 错误 C2825: '_Ret': 必须是一个类或命名空间时跟 '::' 1>d:\dev\coroutinestest\main.cpp(10): 注意:见参考 类模板实例化 'std::experimental::coroutine_traits' 正在编译 1>c:\程序文件(x86)\微软视觉 studio\2017\professional\vc\tools\msvc\14.10.25017\include\experimental\resumable(46): 错误 C2510:“_Ret”:“::”左侧必须是类/结构/联合 1>c:\程序文件(x86)\微软视觉 studio\2017\professional\vc\tools\msvc\14.10.25017\include\experimental\resumable(46): 错误 C2061:语法错误:标识符 'promise_type' 1>c:\program 文件(x86)\微软视觉 studio\2017\professional\vc\tools\msvc\14.10.25017\include\experimental\resumable(46): 错误 C2238:';' 前面的意外标记

现在我假设这个错误是由于 void::promise_type 是荒谬的,但是为什么当没有任何东西可以返回时,promise 类型甚至被实例化?我希望能够从协程中返回任何内容。这只是实现中的一个当前错误还是我误解了协程的使用。

谢谢

【问题讨论】:

  • 感谢您的回复。我阅读过的所有谈话/文档都显示协程返回您希望的任何非 void 类型(假设它符合一些基本标准)。但是他们没有明确说明为什么 void 对协程无效。我可以完全想象一个处理作业的协程(使用 co_await 将作业放在工作人员的队列中)并且当它返回时,不想返回值。正如您所建议的那样,尽管我可能会求助于 future 或其他一些虚拟类型作为解决方法,但这看起来确实是这样,充其量是一种解决方法,最坏的情况是一个错误。
  • 谢谢。这大概就是问题所在。我观看并阅读了一些关于 MS 提案的演示文稿/帖子,但我没有得到必须有非 void 返回类型的印象。自从发布以来,我找到了 Gor 关于协程的措辞,是的,它确实指定了返回类型是必需的。此后,我还阅读了 Chris Kohloff 和其他人对该提案的关注的其他帖子,似乎 MS 的实施目前可能存在相当大的缺陷,需要工作。因此我想我会避免在任何生产代码中使用它。

标签: c++ visual-studio visual-studio-2017 coroutine


【解决方案1】:

当然可以返回void。原因是 - 没有人为void 实现协程协议。你可以自己实现它。通过为coroutine_traits 提供特化来满足返回类型的协程协议。

要使void 成为协程的有效返回类型,您可以这样做:

namespace std::experimental
{
    template<class... T>
    struct coroutine_traits<void, T...>
    {
        struct promise_type
        {
            void get_return_object() {}

            void set_exception(exception_ptr const&) noexcept {}

            bool initial_suspend() noexcept
            {
                return false;
            }

            bool final_suspend() noexcept
            {
                return false;
            }

            void return_void() noexcept {}
        };
    };
}

这允许您的示例编译。

但是,请注意,在您的示例中,调用 co_await suspend_always{}; 会导致内存泄漏,这是因为 coroutine_handle 很像一个原始指针,您需要自己确保协程被销毁。

附带说明一下,协程仿真库CO2 做出了另一个决定——它的co2::coroutine 拥有唯一所有权,因此对CO2_AWAIT(suspend_always{}); 的调用不会泄漏内存,它只是取消了协程。

【讨论】:

    【解决方案2】:

    我在尝试调试编译器错误消息时偶然发现了这个问题。我的解决方案是让函数返回 IAsyncAction 而不是返回 void

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-10
      • 2011-12-09
      • 2010-11-24
      • 2011-12-17
      • 2011-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多