3

我正在学习 msdn (链接) 上的“教程:使用 C++ 创建您的第一个 Metro 风格应用程序”。不久进入它的“第 2 部分”,我遇到了一个错误。我在 Windows 8 VM Release Preview(5 月 31 日)和 Visual Studio 2012 Release Candidate(最新)上运行它。

在添加了 3 个新的 Metro 页面、ItemsPage、SplitPage 和新的“DetailPage”之后,我所在的位置是代码部分。添加这些很好,但是当我直接在下面添加代码时,在标记为“修改初始化数据模型的异步代码”的部分中,它会创建以下错误的两个副本:

error C2893: Failed to specialize function template ''unknown-type' Concurrency::details::_VoidReturnTypeHelper(_Function,int,...)' c:\program files (x86)\microsoft visual studio 11.0\vc\include\ppltasks.h   404 1   SimpleBlogReader

然后我从该部分中取出所有代码并开始一次添加一段以找出错误“真正”在哪里,因为我显然没有修改那个标准头文件。原来它在App::InitDataSource方法的任务链中:

SyndicationClient^ client = ref new SyndicationClient();   

for(wstring url : urls)
{
    // Create the async operation. 
    // feedOp is an IAsyncOperationWithProgress<SyndicationFeed^, RetrievalProgress>^
    auto feedUri = ref new Uri(ref new String(url.c_str()));
    auto feedOp = client->RetrieveFeedAsync(feedUri);

    // Create the task object and pass it the async operation.
    // SyndicationFeed^ is the type of the return value
    // that the feedOp operation will eventually produce.       

    // Then, initialize a FeedData object with the feed info. Each
    // operation is independent and does not have to happen on the
    // UI thread. Therefore, we specify use_arbitrary.
    create_task(feedOp).then([this]  (SyndicationFeed^ feed) -> FeedData^
    {
        return GetFeedData(feed);
    }, concurrency::task_continuation_context::use_arbitrary())

        // Append the initialized FeedData object to the list
        // that is the data source for the items collection.
        // This has to happen on the UI thread. By default, a .then
        // continuation runs in the same apartment thread that it was called on.
        // Because the actions will be synchronized for us, we can append 
        // safely to the Vector without taking an explicit lock.
        .then([fds] (FeedData^ fd)
    {
        fds->Append(fd);

        // Write to VS output window in debug mode only. Requires <windows.h>.
        OutputDebugString(fd->Title->Data());
        OutputDebugString(L"\r\n");
    })

        // The last continuation serves as an error handler. The
        // call to get() will surface any exceptions that were raised
        // at any point in the task chain.
        .then( [this] (concurrency::task<SyndicationFeed^> t)
    {
        try
        {
            t.get();
        }
        // SyndicationClient throws E_INVALIDARG 
        // if a URL contains illegal characters.
        catch(Platform::InvalidArgumentException^ e)
        {
            // TODO handle error. For example purposes
            // we just output error to console.
            OutputDebugString(e->Message->Data());
        }
    }); //end task chain

我一次取出一个 lambdas(并放入分号以便编译),如果我有前两个,那很好,但链中的最后一个会导致错误。但如果我create_task只是最后一个,它会编译。或者如果我用它编译的第一个和第三个来做。或者只有前两个。

问题是第二个 lambda 吗?头库是否对 void 返回类型感到困惑?或者是别的什么?根据这个理论,我将“最终”处理程序声明修改为:

        // The last continuation serves as an error handler. The
        // call to get() will surface any exceptions that were raised
        // at any point in the task chain.
        .then( [this] (concurrency::task<void> t)

现在编译。但是根据 msdn 上的文档(这里),这是对的吗?该页面上有一个名为“基于价值的与基于任务的延续”的部分,复制如下:

给定一个返回类型为 T 的任务对象,您可以为其后续任务提供一个类型为 T 或任务的值。采用 T 类型的延续称为基于值的延续。当前面的任务没有错误地完成并且没有被取消时,将安排一个基于值的延续来执行。将类型任务作为其参数的延续称为基于任务的延续。基于任务的延续总是安排在前面的任务完成时执行,即使前面的任务被取消或抛出异常也是如此。然后,您可以调用 task::get 来获取先前任务的结果。如果前面的任务被取消,task::get 抛出 concurrency::task_canceled。如果前面的任务抛出异常,task::get 会重新抛出该异常。

这是说错误处理的最终延续应该是最终.then延续的类型,还是原始延续的类型create_task?如果它是最终的(就像我在上面所做的那样void),这种延续实际上会处理所有上述错误,还是仅处理最终.then调用的错误?

这是“修复”他们的例子的正确方法吗?或不?

4

1 回答 1

2

我认为问题在于您需要来自第二个 lambda 的返回类型,该类型将被馈送到第三个(请参阅FeedData第一个任务的返回类型和第二个任务的参数类型的匹配)。由于第二个任务不返回任何东西void似乎是正确的选择。由于您想使用第三个来捕获错误,因此您需要使用concurrency::task<void>(基于报价)。

此外,根据引用,如果前面的(本例中的第二个)任务失败,则将调用最终任务,并在调用时报告其执行期间发生的任何错误t.get()。我不确定第一个失败的情况,但是您可以通过从第一个任务中抛出任意异常来尝试发生什么。

于 2012-06-07T15:09:36.303 回答