是否有一种方法可以机械地识别在移出对象上哪些操作是安全的

Is there a way to mechanically identify which operations are safe on a moved-from object?

本文关键字:对象 移出 安全 操作 识别 一种 机械 方法 是否      更新时间:2023-10-16

可移动性允许大量的优化。然而,它感觉这样做的代价是在程序的静态安全上打了一个洞:

移动后,源对象保持在一个有效但未指定的状态,其中一些操作是合法的,但有些是不合法的。(关于这个主题的讨论,请参见这个SO问题)。尽管这个操作列表依赖于每种类型,但似乎可以在编译时知道它。然而,编译器不会对移动自对象的错误使用发出警告(正如另一个SO问题所讨论的)。

感觉就像c++的哲学一样,依赖编译器来尽可能多地验证(静态已知的),其中一个例子就是强制执行常量正确性。然而,似乎从移动对象可以在危险的情况下使用,而编译器不会尝试(或没有任何意图)捕获它们。

是否有一种机制允许编译器更好地诊断?如果没有,为什么没有一个新的限定符应用于可用于从对象移动的方法,或者另一种允许等效静态验证的机制呢?

Moveability允许大量的优化。然而,它感觉这样做的代价是在程序的静态安全上打了一个洞。

是的,它有。

尽管这个操作列表依赖于每种类型,但似乎可以在编译时知道它。

但通常不是由编译器执行的。

但是,编译器不会警告不正确地使用移出对象。

那就太好了!

不,您将不得不依赖文档。或者就像我所做的那样,从左值移动后,不再使用该对象,除非你有一个严格控制的作用域,并且在对象上立即进行了一些明显的"重置"操作。

当然,从右值移动不会出现这个问题。

我认为不太可能在标准中添加一些额外的限定符来处理这样的边缘情况。

一种可能是用#pragma supports_moved_from或其他东西标记你的函数声明,然后构建一个静态分析工具来检测对可能从对象移动的函数的调用,并检查它们是否被标记。

您可以使用众多Clang工具接口中的一个来完成此操作。

给定n4034和std::experimental::optional,您可以想象一个具有移出并清空操作的可选项。

这样的对象在被移动后将会处于一个明确的"not valid"状态。

你仍然需要某种方式来表达状态变化,使c++编译器能够静态地检查它。

理论上,允许操作在变量的生命周期内改变其类型的语言扩展可以添加到c++中,以及类型注释;然后,可以更改移出值以应用该注释,并且对移出值无效的操作将触发编译器错误。

.reset()样式的操作可能对移出值和非移出值都有效,并且在这两种情况下都将注释转换为"正常"。

我不是专家,但我相信Rust试图做这样的事情来解决一些类似的问题;程序员必须向类型系统证明某些操作是有效的。

这也类似于最近对泄漏/无效指针静态检测的工作。

任何存在的对象,如果处于允许某些操作而不允许其他操作的状态,则可能存在设计问题。通常,这是违反单一责任原则的强烈迹象。至少所有的成员函数在任何情况下都应该有一个定义好的行为——也许它会抛出一个异常。

所以当你问,在一个特定的状态下你可以对一个对象调用哪些操作时,重新考虑你的设计。

是否有一种机制允许编译器更好地诊断?

没有什么可以阻止编译器尝试对移出对象的滥用进行静态分析:这相当于将对象重新标记为处于未初始化状态,并且许多编译器会在使用它们不确定已初始化的变量时发出警告。

也就是说,标准通常不强制这些类型的诊断:它们在编译时间上可能是昂贵的,它们通常是不完美的(例如,如果你将一个非const引用传递给一个从移动对象到你调用的函数,并且实现不在翻译单元中(即对编译器不可见)-它不能知道从移动对象是否可能被"使用",而没有某种事先赋值/重置操作到一个有意义的值。

同样,如果你将一个非const的对象引用传递给不知道其定义的函数,你无法知道该对象是否可以从函数内部移动。

如果没有,为什么没有一个新的限定符应用于可以用于从对象移动的方法,或者另一种允许等效静态验证的机制?

底线是,有一些时候,编译器相对容易识别一个对象是一个移出状态,而很多时候,它不能。限定词可能很有用,但毫无疑问,它会被许多人误认为是移动对象不会被滥用的保证,并且会出现持续不断的相关问题和漏洞。没有尽力而为的验证——并让程序员清楚地理解分析问题是他们的责任——有时比给开发人员一种错误的安全感要好。