对于本地作用域中的任何对象,我的默认行为是使其为const。例如:
auto const cake = bake_cake(arguments);我尽量少使用非功能代码,因为这增加了可读性(并为编译器提供了一些优化机会)。因此,在类型系统中也反映这一点是合乎逻辑的。
然而,使用move语义,这就产生了一个问题:如果我的cake很难或不可能复制,并且我想在它完成后将它传递出去,怎么办?例如:
if (tastes_fine(cake)) {
return serve_dish(cake);
}据我所知,复制省略规则并不保证cake副本将被删除(但我不确定这一点)。
所以,我得把cake搬出去:
return serve_dish(std::move(cake)); // this will not work as intended但是,std::move将一事无成,因为它(正确无误)不会把Cake const&投给Cake&&。即使物体的生命周期已经接近尾声了。我们不能从我们承诺不改变的事情中窃取资源。但这将削弱康斯特的正确性。
那么,我怎么才能把蛋糕也吃了呢?
(也就是说,我如何才能拥有const-正确性,并从移动语义中获益)。
发布于 2020-05-24 16:35:10
我认为不可能从const对象中移动,至少使用标准的move构造函数和非mutable成员。但是,可以为其提供一个const自动本地对象和应用 (即NRVO)。在您的示例中,您可以重写原始函数如下:
Cake helper(arguments)
{
const auto cake = bake_cake(arguments);
... // original code with const cake
return cake; // NRVO
}然后,在原来的函数中,只需调用:
return serve_dish(helper(arguments));因为helper返回的对象已经是一个非const值,所以它可能会被移动-从(如果适用的话,也可能被省略)。
这里是一个演示这种方法的现场演示。注意,在生成的程序集中没有调用复制/移动构造函数。
发布于 2020-08-17 13:59:09
如果可以的话,让它们移动。
是时候改变你的“默认行为”了,因为它是不合时宜的。
如果从一开始就将移动语义内置到语言中,那么自动变量const就会很快被确定为糟糕的编程实践。
const从来没有被用于微观优化。微型优化最好留给编译器。const主要用于成员变量和成员函数。它还帮助清理了一些语言:例如,"foo"是const char[4]类型,而在C中是char[4]类型,有一种奇怪的理解,即不允许修改内容。
现在(由于C++11)用于自动变量的const实际上可能有害,正如您所观察到的,现在是停止这种做法的时候了。const参数按值类型也可以这样说.您的代码也不会那么冗长。
就我个人而言,比起const对象,我更喜欢不变的对象。
发布于 2020-08-18 09:57:19
您确实应该继续将变量设置为常量,因为这是一个很好的实践(称为康斯特-正确性),而且在对代码进行推理时--甚至在创建代码时--这也很有帮助。常量对象不能移动--这是一件好事--如果您从一个对象移动,您几乎总是在很大程度上修改它,或者至少这是隐含的(因为移动基本上意味着窃取原始对象所拥有的资源)!
来自核心准则
你不可能有一个常量的比赛条件。当许多对象无法更改它们的值时,对程序的推理就更容易了。作为参数传递的对象的“不改变”的接口大大提高了可读性。
尤其是http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rconst-const
Con.4:使用const定义在构造后不更改的值的对象
接着是问题的下一个主要部分:
Is there a solution that does not exploit NRVO?如果由NRVO你采取包括保证复制省略,那么不是真的,或是和否在同一。这有点复杂。尝试按值函数将返回值移出返回值并不一定能按您的想法或要求进行。此外,“没有副本”总是比移动性能好。因此,您应该尝试让编译器执行它的魔力,并特别依赖于保证的复制省略(因为您使用了c++17)。如果您有一个我称之为不可能省略的复杂场景:然后可以使用一个move与保证的复制省略/NRVO相结合,以避免一个完整的副本。
因此,这个问题的答案是:,如果您的对象已经声明为const,那么您几乎总是可以直接依赖于复制-省略/返回值,所以请使用它。否则,您将有其他一些场景,然后对最佳方法使用谨慎性--在罕见的情况下,可以使用move (这意味着它与复制-elision相结合)。
“复杂”场景的示例:
std::string f() {
std::string res("res");
return res.insert(0, "more: ");//'complex scenario': a reference gets returned here will usually mean a copy is invoked here.
}“修复”的最佳方法是使用复制-省略,即:
return res;//just return res as we already had that thus avoiding copy altogether - it's possible that we can't use this solution for more *hairy/complex* scenarios.在这个例子中,“修复”的次等方法是;
return std::move(res.insert(0, "more: "));https://stackoverflow.com/questions/61987624
复制相似问题