【发布时间】:2020-09-11 05:14:50
【问题描述】:
对于本地范围内的任何对象,我的默认行为是将其设为 const。例如:
auto const cake = bake_cake(arguments);
我尽量减少非功能性代码,以提高可读性(并为编译器提供一些优化机会)。因此,在类型系统中也反映这一点是合乎逻辑的。
但是,使用移动语义,这会产生问题:如果我的cake 难以复制或无法复制,而我想在完成后将其传递出去怎么办?例如:
if (tastes_fine(cake)) {
return serve_dish(cake);
}
据我了解 copy elision rules 不能保证 cake 副本会被删除(但我不确定)。
所以,我必须将cake 移出:
return serve_dish(std::move(cake)); // this will not work as intended
但是 std::move 将 do nothing useful,因为它 (correctly) 不会将 Cake const& 转换为 Cake&&。即使对象的生命周期非常接近尾声。我们不能从我们承诺不会改变的东西中窃取资源。但这会削弱 const 正确性。
那么,我怎样才能把蛋糕也吃掉呢?
(即我如何才能拥有 const 正确性并从移动语义中受益。)
【问题讨论】:
-
如果你真的想,你可以写一个包装类,里面有一个非常量实例,它公开一个对它的常量引用和一个
move_from方法。在调试版本中,您可以添加一个断言以防止对象在被移出后被使用。但老实说,我会简单地放弃const并完成它。 -
我唯一想到的就是使用 PIMPL 成语,并将
unique_ptr转换为 implmutable,或者移动构造 impl,作为元素unique_ptr点to 不会“继承”常量。这两件事都有些笨拙。 -
@HolyBlackCat 是的,我考虑过这一点,但我发现它存在三个问题。首先,它不会直观地告诉您发生了什么,我仍然必须写
W<Cake> cake而不是Cake const cake(这可以通过将持有的实例设置为mutable来缓解)。其次,auto在窗外。第三,它剥夺了编译器的优化机会,因为持有的实例不再是const(它只是穿上了 const 外观的衣服)。 -
@DanielLangr:
const的优化也丢失了。 (通过 const 引用进行变异仍然是可能的,而 mutate const 对象是 UB)。 -
@DanielLangr:我们可以节省一些
load。在foo(const int&); const int i = 42; foo(i); return i;我们知道我们返回 42;在int i = 42; foo(i); return i;中,我们必须重新加载i,这可能在foo中发生了变化。
标签: c++ c++17 move-semantics const-correctness