可移动性允许一系列优化。然而,它认为它是以牺牲程序的静态安全性为代价来实现的:
移动后,源对象处于有效但未指定的状态,其中某些操作是合法的,但有些则不合法。 (特别是有关此主题的讨论,请参阅this SO question)。看起来这个操作列表,即使它依赖于每种类型,也可以在编译时知道。然而,编译器没有警告移动对象的错误使用(如this other SO question讨论的那样)。
感觉就像C ++哲学一样,依靠编译器尽可能多地验证(静态知道的),其中一个例子是const-correctness enforcing。 然而,似乎移动对象可以以危险的方式使用,而编译器不会尝试(或有任何意义)来捕获它们。
实际上是否存在允许编译器更好诊断的机制? 如果没有,为什么没有新的限定符应用于可以在移动的对象上使用的方法,或者是否允许等效静态验证的其他机制?
答案 0 :(得分:4)
可移动性允许一系列优化。然而,它认为这样做的代价是打破了程序的静态安全漏洞。
是的,确实如此。
看起来这个操作列表,即使它依赖于每种类型,也可以在编译时知道。
但一般而言,不是编译器。
然而,编译器并未警告移动对象的错误使用。
但这会很好!
不,你将不得不依赖文档。或者只是做我做的事情,并且从左值移动后,再也不会使用该对象除非你有一个严格控制的范围,并且此后立即对该对象进行一些明显的“重置”操作。
当然,从右值移动不会出现这个问题。
答案 1 :(得分:1)
我认为这样的边缘案例的标准中不太可能添加一些额外的限定符。
一种可能性是用#pragma supports_moved_from
标记你的函数声明,然后构建一个静态分析工具来检测对可能移动的对象的函数的调用,并检查它们是否被标记。
您可以使用众多Clang tool interfaces中的一个来实现此目的。
答案 2 :(得分:1)
鉴于n4034和std::experimental::optional
,您可以想象一个可选的具有移动和空操作。
这样的物体在移出后会处于明显的“无效”状态。
您仍然需要某种方式来表达状态更改,以便C ++编译器能够静态检查它。
理论上,允许操作在其生命周期内改变变量类型的语言扩展可以添加到C ++,以及类型注释;然后可以更改移动的值以应用该注释,对移动的值无效的操作将触发编译器错误。
.reset()
样式操作可能对移动的值和非移动的值都有效,并且在两种情况下都将注释转换为“正常”。
我不是专家,但我相信Rust会尝试这样做来解决类似的问题;程序员必须向类型系统证明某些操作是有效的。
这与泄漏/无效指针静态检测的recent work类似。
答案 3 :(得分:0)
每当您的任何对象(确实存在)处于某种状态,允许某些操作而其他操作都不存在时,您可能会遇到设计问题。通常这是违反单一责任原则的强烈信号。
,至少所有成员函数在任何情况下都应该有一个定义的行为 - 也许它会抛出异常。因此,每当您询问允许哪些操作调用特定状态的对象时,请重新考虑您的设计。
答案 4 :(得分:0)
实际上有一种机制可以让编译器更好地进行诊断吗?
没有什么可以阻止编译器尝试对移动对象的滥用进行静态分析:它与将对象重新标记为处于未初始化状态的情况大致相同,并且许多编译器会发出警告使用他们不自信的变量已被初始化。
也就是说,标准通常不会强制要求这些诊断:它们在编译时可能很昂贵,它们通常是不完美的(例如,如果你通过非{{1}引用移动对象到你调用的函数,并且实现不在翻译单元中(即编译器不可见) - 它无法知道移动对象是否可能是"使用"没有某种先前的赋值/重置操作到有意义的值。
同样,如果您将任何对象的非const
引用传递给任何定义未知的函数,您就无法知道该对象是否可能从该内部移动功能
如果没有,为什么没有新的限定符应用于可以在移动的对象上使用的方法,或者其他允许等效静态验证的机制?
最重要的是,有几次,编译器相对容易识别对象是移动状态,而且很多时候它不能。限定符可能是有用的,但毫无疑问,它被误解为很多人,以保证移动的对象不会被滥用,并且不断有相关问题和虫子。没有尽力而为的验证 - 让程序员清楚地了解他们有责任分析问题 - 有时候会比给开发人员一种虚假的安全感更好。