有没有办法机械地识别移动对象上哪些操作是安全的?

时间:2015-10-16 09:02:12

标签: c++ c++11 static move type-safety

可移动性允许一系列优化。然而,它认为它是以牺牲程序的静态安全性为代价来实现的:

移动后,源对象处于有效但未指定的状态,其中某些操作是合法的,但有些则不合法。 (特别是有关此主题的讨论,请参阅this SO question)。看起来这个操作列表,即使它依赖于每种类型,也可以在编译时知道。然而,编译器没有警告移动对象的错误使用(如this other SO question讨论的那样)。

感觉就像C ++哲学一样,依靠编译器尽可能多地验证(静态知道的),其中一个例子是const-correctness enforcing。 然而,似乎移动对象可以以危险的方式使用,而编译器不会尝试(或有任何意义)来捕获它们。

实际上是否存在允许编译器更好诊断的机制? 如果没有,为什么没有新的限定符应用于可以在移动的对象上使用的方法,或者是否允许等效静态验证的其他机制?

5 个答案:

答案 0 :(得分:4)

  

可移动性允许一系列优化。然而,它认为这样做的代价是打破了程序的静态安全漏洞。

是的,确实如此。

  

看起来这个操作列表,即使它依赖于每种类型,也可以在编译时知道。

但一般而言,不是编译器。

  

然而,编译器并未警告移动对象的错误使用。

但这会很好!

不,你将不得不依赖文档。或者只是做我做的事情,并且从左值移动后,再也不会使用该对象除非你有一个严格控制的范围,并且此后立即对该对象进行一些明显的“重置”操作。

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

答案 1 :(得分:1)

我认为这样的边缘案例的标准中不太可能添加一些额外的限定符。

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

您可以使用众多Clang tool interfaces中的一个来实现此目的。

答案 2 :(得分:1)

鉴于n4034std::experimental::optional,您可以想象一个可选的具有移动和空操作。

这样的物体在移出后会处于明显的“无效”状态。

您仍然需要某种方式来表达状态更改,以便C ++编译器能够静态检查它。

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

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

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

这与泄漏/无效指针静态检测的recent work类似。

答案 3 :(得分:0)

每当您的任何对象(确实存在)处于某种状态,允许某些操作而其他操作都不存在时,您可能会遇到设计问题。通常这是违反单一责任原则的强烈信号。

,至少所有成员函数在任何情况下都应该有一个定义的行为 - 也许它会抛出异常。

因此,每当您询问允许哪些操作调用特定状态的对象时,请重新考虑您的设计。

答案 4 :(得分:0)

  

实际上有一种机制可以让编译器更好地进行诊断吗?

没有什么可以阻止编译器尝试对移动对象的滥用进行静态分析:它与将对象重新标记为处于未初始化状态的情况大致相同,并且许多编译器会发出警告使用他们不自信的变量已被初始化。

也就是说,标准通常不会强制要求这些诊断:它们在编译时可能很昂贵,它们通常是不完美的(例如,如果你通过非{{1}引用移动对象到你调用的函数,并且实现不在翻译单元中(即编译器不可见) - 它无法知道移动对象是否可能是"使用"没有某种先前的赋值/重置操作到有意义的值。

同样,如果您将任何对象的非const引用传递给任何定义未知的函数,您就无法知道该对象是否可能从该内部移动功能

  

如果没有,为什么没有新的限定符应用于可以在移动的对象上使用的方法,或者其他允许等效静态验证的机制?

最重要的是,有几次,编译器相对容易识别对象是移动状态,而且很多时候它不能。限定符可能是有用的,但毫无疑问,它被误解为很多人,以保证移动的对象不会被滥用,并且不断有相关问题和虫子。没有尽力而为的验证 - 让程序员清楚地了解他们有责任分析问题 - 有时候会比给开发人员一种虚假的安全感更好。