Com*_*sMS 11 c++ idioms move-semantics exception-safety c++11
我有一个函数可以作为接收器参数传入大量数据.我的BigData类型已经是C++ 11感知的,并且具有全功能的移动构造函数和移动赋值实现,所以我可以逃脱而不必复制该死的东西:
Result processBigData(BigData);
[...]
BigData b = retrieveData();
Result r = processBigData(std::move(b));
Run Code Online (Sandbox Code Playgroud)
一切都很好.但是,我的处理函数可能偶尔会在运行时失败,从而导致异常.这不是一个真正的问题,因为我可以修复东西并重试:
BigData b = retrieveData();
Result r;
try {
r = processBigData(std::move(b));
} catch(std::runtime_error&) {
r = fixEnvironmnentAndTryAgain(b);
// wait, something isn't right here...
}
Run Code Online (Sandbox Code Playgroud)
当然,这不起作用.
由于我将数据移动到处理函数中,所以当我到达异常处理程序时,b将不再可用.
这可能会大大降低我对以次值传递次要参数的热情.
所以问题是:如何在现代C++代码中处理这样的情况?如何检索以前移入无法执行的函数的数据访问?
您可以更改实施和接口都BigData和processBigData你请.然而,最终的解决方案应该尽量减少原始代码在效率和可用性方面的缺点.
显然,这个问题在最近的 CppCon 2014 上得到了热烈的讨论。Herb Sutter 在他的闭幕演讲《回到基础!》中总结了最新的情况。现代 C++ 风格要点(幻灯片)。
他的结论非常简单:不要对接收器参数使用按值传递。
首先使用该技术的争论(正如 Eric Niebler 的 Meeting C++ 2013 主题演讲C++11 库设计(幻灯片)所普及的那样)似乎被其缺点所抵消。按值传递接收器参数的最初动机是消除因使用const&/导致的函数重载的组合爆炸&&。
不幸的是,这似乎带来了许多意想不到的后果。其中之一是潜在的效率缺陷(主要是由于不必要的缓冲区分配)。另一个是来自这个问题的异常安全问题。赫伯的演讲中讨论了这两个问题。
Herb 的结论是不要对接收器参数使用按值传递,而是依赖单独的const&/ &&(const&默认值是&&为少数需要优化的情况保留的)。
这也符合@Potatoswatter 的回答建议。通过通过 via 传递接收器参数,&&我们也许能够将数据从参数的实际移动推迟到我们可以提供 noexcept 保证的点。
我有点喜欢按值传递接收器参数的想法,但似乎它在实践中并不像每个人希望的那样有效。
思考了5年之后更新:
我现在确信我的激励示例是对移动语义的滥用。在调用 后processBigData(std::move(b));,我永远不应该被允许假设 的状态是什么b,即使该函数因异常退出。这样做会导致代码难以遵循和维护。
相反,如果在错误情况下应该可以恢复 的内容b,则需要在代码中明确说明。例如:
class BigDataException : public std::runtime_error {
private:
BigData b;
public:
BigData retrieveDataAfterError() &&;
// [...]
};
BigData b = retrieveData();
Result r;
try {
r = processBigData(std::move(b));
} catch(BigDataException& e) {
b = std::move(e).retrieveDataAfterError();
r = fixEnvironmnentAndTryAgain(std::move(b));
}
Run Code Online (Sandbox Code Playgroud)
如果我想恢复 的内容b,我需要沿着错误路径显式地将它们传递出去(在本例中包裹在 内BigDataException)。这种方法需要一些额外的样板,但它更惯用,因为它不需要对移出对象的状态做出假设。
| 归档时间: |
|
| 查看次数: |
482 次 |
| 最近记录: |