注意:此答案仅适用于c ++ 11及更高版本。没有“ C / C ++”之类的东西,它们是不同的语言。
不,按值返回本地对象没有危险,建议这样做。但是,我认为这里所有答案中都缺少一个重要的观点。许多其他人说该结构是使用RVO复制或直接放置的。但是,这并不完全正确。我将尝试解释返回本地对象时可能发生的事情。
移动语义
从c ++ 11开始,我们有了右值引用,这些引用是可以从安全地窃取的临时对象的引用。例如,std :: vector具有移动构造函数和移动赋值运算符。两者都具有恒定的复杂度,并且只需将指针复制到要从其移出的向量的数据即可。在这里,我不会详细介绍移动语义。
因为在函数内本地创建的对象是临时的,并且在函数返回时超出范围,所以永远不会从c ++ 11开始复制返回的对象。在返回的对象上调用move构造函数(或以后不再说明)。这意味着,如果要使用昂贵的复制构造函数但使用廉价的move构造函数(如大向量)返回对象,则仅将数据所有权从本地对象转移到返回的对象上-这很便宜。
请注意,在您的特定示例中,复制和移动对象没有区别。结构的默认移动和复制构造函数将执行相同的操作。复制两个整数。但是,这至少比任何其他解决方案都快,因为整个结构都适合64位CPU寄存器(如果我输入错了,请更正我,我不知道很多CPU寄存器)。
RVO和NRVO
RVO意味着“返回值优化”,是编译器进行的会产生副作用的极少数优化之一。从c ++ 17开始,需要RVO。返回未命名的对象时,将直接在原位构造该函数,调用者将其分配返回的值。复制构造函数和move构造函数均未调用。如果没有RVO,则将首先在本地构造未命名对象,然后在返回的地址中移动构造,然后销毁本地未命名对象。
需要RVO(c ++ 17)或可能(在c ++ 17之前)的示例:
auto function(int a, int b) -> MyStruct {
return MyStruct{a, b};
}
NRVO的意思是命名返回值优化,它与RVO相同,只不过它是针对调用函数本地的命名对象完成的。标准(c ++ 20)仍不能保证这一点,但是许多编译器仍然可以做到。请注意,即使使用已命名的本地对象,返回时最坏的情况是它们也会被移动。
结论
唯一不考虑按值返回的情况是当您有一个命名的,非常大的对象(如其堆栈大小)时。这是因为尚未保证NRVO(从c ++ 20开始),甚至移动对象也很慢。我的建议以及《Cpp核心准则》中的建议始终是按值返回对象(如果有多个返回值,则使用struct(或tuple)),唯一的例外是对象移动成本很高。在这种情况下,请使用非常量引用参数。
返回必须从c ++中的函数手动释放的资源永远不是一个好主意。绝对不要那样做。至少使用std :: unique_ptr,或使用释放其资源(RAII)并返回其实例的析构函数来制作自己的非本地或本地结构。如果资源没有自己的移动语义(并删除副本构造函数/赋值),则定义移动构造函数和移动赋值运算符也是一个好主意。