我正在学习 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
调用的错误?
这是“修复”他们的例子的正确方法吗?或不?