我正在尝试编写一个合同设计库。 我的一种类型定义为:
#include <stdexcept>
#include <type_traits>
#include <utility>
template <typename T, typename Post>
class out {
public:
out(T& param, Post check = {})
: param{param},
copy{param}, //must be copyable; may throw
check{std::move(check)}
{}
~out() noexcept(false) {
if (!check(param)) {
param = std::move(copy); //rollback; assume this never throws
if (!std::uncaught_exceptions()) //no exception currently being thrown
throw std::invalid_argument("postcondition violated"); // leaves destructor if another exception is not being thrown
}
}
private:
T& param;
T copy;
Post check;
};
以下是如何使用它的示例:
#include <vector>
#include <cassert>
struct is_not_empty {
bool operator()(const std::vector<int>& v) { return !v.empty(); }
/*not real code, just meant to illustrate the problem*/
is_not_empty() { /*acquire some resource*/ }
~is_not_empty() { /*release some resource*/ }
/******/
};
void function_with_postcondition(out<std::vector<int>, is_not_empty> v) {
} // Exception thrown here in out<>::~out
int main() {
std::vector<int> v; //empty
try {
function_with_postcondition(v);
assert(false); //not reached
}
catch (const std::invalid_argument& e) {
}
}
我的问题 -
v
对象是否已在 function_with_postcondition()
中正确销毁? out<>::Post
成员是否被正确销毁? 在这个例子中是否存在资源泄漏?我是否递归调用析构函数? 这是明确定义的吗? 我真正想要了解的是确切发生的操作的(明确定义/未定义)顺序。
我知道堆栈展开,并且我知道在已经抛出异常时抛出异常会导致对
std::terminate()
的调用 - 这就是我在析构函数中调用 std::uncaught_exceptions()
时试图避免的情况。 如果函数抛出异常,我不想检查后置条件,因为函数异常退出。
最后,为什么不采用另一种方式(例如
assert()
、abort()
、exit()
等)? 我正在尝试编写一个库来检查在异常环境中稳健的函数前置条件和后置条件。 我正在尝试减少所需的样板数量,同时允许允许重试操作的提交/回滚语义。