我读到了有关合同的内容关于 C++17 的思考 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4492.pdf由 B. Stroustrup 撰写,并协助进行了一次小型演讲,讨论了它们,但我不确定我是否真正理解了它们。
所以我有一些疑问,是否可以用一些例子来说明它们:
-
合同只是经典合同的更好替代品吗?assert()
,它们应该一起使用吗?对于软件开发人员来说,哪些合同实际上是简单的?
-
合同会影响我们处理异常的方式吗?如果是,我们应该如何使用例外和合同?
-
使用合约是否意味着执行时的开销?我们可以在发布代码上停用它们吗?
来自提案 N4415 http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4415.pdf:
Vector 类的索引运算符的前置条件契约可以编写为:
T& operator[](size_t i) [[expects: i < size()]];
类似地,ArrayView 类的构造函数上的后置条件契约可以表示为:ArrayView(const vector<T>& v) [[ensures: data() == v.data()]];
感谢@Keith Thompson 评论:
合约没有进入 C++20. 一个新的研究小组 SG21 已经成立。 https://herbsutter.com/2019/07/20/trip-report-summer-iso-c-standards-meeting-cologne/
据我从这份文件中读到:http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4415.pdf http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4415.pdf
合同做什么assert
多年来一直尝试以原始方式进行。它们既是文档,也是运行时断言,说明调用者应该如何调用该函数,以及调用者期望函数返回后代码处于什么状态。这些通常称为前置条件和后置条件,或不变量。
这有助于清理实现方面的代码,因为通过合约,我们可以假设一旦执行进入您的函数,您的参数就处于有效状态(您期望它们是什么)。
后置条件部分可能会改变您处理异常的方式,因为对于合约,您必须确保抛出异常不会破坏您的后置条件。这通常意味着您的代码必须是异常安全的,尽管这意味着强异常保证还是基本保证取决于您的条件。
Example:
class Data;
class MyVector {
public:
void MyVector::push_back(Elem e) [[ensures: data != nullptr]]
{
if(size >= capacity)
{
Data* p = data;
data = nullptr; // Just for the sake of the example...
data = new Data[capacity*2]; // Might throw an exception
// Copy p into data and delete p
}
// Add the element to the end
}
private:
Data* data;
// other data
};
在这个例子中,如果new
or Data
的构造函数抛出异常,您的后置条件被违反。这意味着您应该更改所有此类代码,以确保您的合同永远不会被违反!
当然,就像assert
,合同可能包括运行时开销。但不同之处在于,由于契约可以作为函数声明的一部分,因此编译器可以进行更好的优化,例如评估调用者站点的条件,甚至在编译时评估它们。本文开头提到的文档的第 1.5 节讨论了根据您的构建配置关闭合约的可能性,就像普通的旧断言一样。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)