假设我想为仅移动类型编写一个接收器容器。根据Meyer和Sutter在http://scottmeyers.blogspot.de/2014/07/should-move-only-types-ever-be-passed.html上的讨论,sink参数应该通过值传递,然后移动到成员中。但在异常安全性方面,我觉得这听起来不是个好主意。当构造函数抛出异常时,接收器参数的值将丢失,而当我通过rvalue引用传递异常并且异常发生在实际移动之前时,调用者仍然可以检索接收器参数的值。
示例如下:
#include <memory>
#include <iostream>
#include <stdexcept>
using widget = std::unique_ptr<int>;
struct sink_1 {
sink_1(widget&& w) {
throw std::runtime_error("error");
w_ = std::move(w);
}
widget w_;
};
struct sink_2 {
sink_2(widget w) {
throw std::runtime_error("error");
w_ = std::move(w);
}
widget w_;
};
int main(int argc, char *argv[])
{
auto w1 = std::make_unique<int>(10);
auto w2 = std::make_unique<int>(20);
try {
sink_1 s1(std::move(w1));
} catch (std::runtime_error &e) {
std::cout << *w1 << std::endl;
}
try {
sink_2 s2(std::move(w2));
} catch (std::runtime_error &e) {
std::cout << *w2 << std::endl; // crashes
}
return 0;
}
这是一个有效和良好的设计吗?
发布于 2016-02-28 14:49:32
你可以说“嗯,我不同意斯科特·迈耶斯的观点。”
这两个例子在所谓的“异常保证”方面是不同的。
sink_1
构造函数具有强大的异常保证-这意味着如果抛出异常,w
将保持不变。
sink_2
构造器有弱的异常保证-这意味着如果抛出异常-我们保证s2
和w
都不会泄漏任何资源。
在我看来,坚持尽可能高的异常保证是最好的,在这种情况下,我将坚持使用sink_1
示例。
并不是说sink_2
不好,我个人会选择第一个版本。例如,我的程序可以是Web服务器。w
可能是传入的超文本传输协议请求。如果抛出异常,使用sink_2
将以非常激进的方式关闭请求。对于不同的sink_1
,将允许我重试,使用不同的代码路径,甚至让我有可能向客户端发送一条像样的消息,说操作失败。
发布于 2016-02-28 14:57:30
不,这不是一个好的设计。
您对w1执行的操作可能会崩溃,也可能不会崩溃,这取决于异常是在w1变为"moved from“之前还是之后抛出的。
在w2上调用std::move
并将其按值传递给sink2::sink_2
之后,它就被“从”(移到函数的参数中)了。访问w2肯定会崩溃。
您应该始终将对std::move的调用视为值即将消失的指示器,并且不要以任何方式使用它(这就是std::unique_ptr优于std::auto_ptr的原因:当您按值传递auto_ptr时,它看起来像一个副本,但它的行为像一个移动。不能通过值传递unique_ptr (它没有复制构造函数),要从命名的unique_ptr迁移,必须使用std:: move )。
https://stackoverflow.com/questions/35683826
复制相似问题