由于按值返回在大多数情况下比返回引用效率低,有哪些按值返回有意义或者是唯一可能的方法的例子?
编辑:我错误地表达了我的问题。我知道对于内置类型,按值返回通常更有效。我主要指的是返回大于指针的用户定义类型的情况。
由于按值返回在大多数情况下比返回引用效率低,有哪些按值返回有意义或者是唯一可能的方法的例子?
编辑:我错误地表达了我的问题。我知道对于内置类型,按值返回通常更有效。我主要指的是返回大于指针的用户定义类型的情况。
你在这里遗漏了重点——通过引用返回并不总是有意义的,也不总是合法的。通过引用返回本地会导致未定义的行为。您可以通过引用返回一个超出函数范围的变量,它可以是:
const
getterconst
static
局部变量(或任何具有静态存储的变量)——通常情况下,仅在通过引用返回的函数中不需要它。NRVO 使按价值回报在实践中可行,因此除非您有明确的测量结果表明按价值回报是瓶颈,否则不要担心。而是担心两者所暗示的语义。
在两种情况下,按值返回的效率并不低: 1如果您正在移动对象 (C++11),2如果编译器可以删除副本或优化返回值(对于内置类型总是可能的,请参阅还有 JaredPar 的回答)。
有几个优点:1在函数调用之前不需要声明变量(这可能并不总是可能的),并且可以通过auto
2检测到返回的变量类型设置返回变量的意图是显而易见的。
void foo(some_type &obj_ref);
some_type bar();
// using reference
some_type A; // requires default constructor for A
foo(A); // modification of A is not obvious
// returning a value
auto B = bar(); // move or copy construction of B
请注意,最后一条语句可以使用返回值优化或移动语义 (C++11)
这是 g++ 在将 -O3 标志用于通过引用返回整数的方法时默认生成的:
void Foo(int& rc) {
rc = 42;
}
变成:
__Z3FooRi:
Leh_func_begin1:
pushq %rbp
Ltmp0:
movq %rsp, %rbp
Ltmp1:
movl $42, (%rdi)
popq %rbp
ret
Leh_func_end1:
这是按值返回的相同方法:
int Foo() {
return 42;
}
变成:
__Z3Foov:
Leh_func_begin1:
pushq %rbp
Ltmp0:
movq %rsp, %rbp
Ltmp1:
movl $42, %eax
popq %rbp
ret
Leh_func_end1:
如您所见,您的前提是有缺陷的。两个版本产生几乎相同的机器代码。
三十年来,人们一直在为 C/C++ 生产优化编译器。您可以假设他们已经想到了任何常见的用例,并尽最大努力确保任何常见的代码模式都能生成最佳代码。永远不要改变你的编码风格来提高性能,除非你掌握了能够明确证明它存在差异的分析结果。太多此类“优化”不仅使代码更难阅读,而且实际上使性能更差。
编辑
这是与更大类型的类似比较。在这里,两种方法都返回以下内容struct
:
struct Bar
{
unsigned int a;
unsigned int b;
unsigned int c;
unsigned int d;
};
这段代码:
void Foo(Bar& rc) {
rc.a = 1;
rc.b = 2;
rc.c = 3;
rc.d = 4;
}
产生这个输出:
__Z3FooR3Bar:
Leh_func_begin1:
pushq %rbp
Ltmp0:
movq %rsp, %rbp
Ltmp1:
movl $1, (%rdi)
movl $2, 4(%rdi)
movl $3, 8(%rdi)
movl $4, 12(%rdi)
popq %rbp
ret
Leh_func_end1:
另一方面,这个按值返回的版本:
Bar Foo() {
Bar rc;
rc.a = 1;
rc.a = 2;
rc.a = 3;
rc.a = 4;
return rc;
}
最终产生更少的机器代码指令:
__Z3Foov:
Leh_func_begin1:
pushq %rbp
Ltmp0:
movq %rsp, %rbp
Ltmp1:
movl $4, %eax
xorl %edx, %edx
popq %rbp
ret
Leh_func_end1:
如果您想自己在 g++ 中进行这些比较,请使用g++ -S -O3
. 其他编译器将具有类似的生成程序集的方法。
我认为您从错误的假设开始这个问题,即按价值返回比其他形式的回报效率低。出于几个原因,情况并非如此
int
,等char
在某些情况下,按值返回效率低下。然而,这并不意味着所有情况都是如此。
初学者的回答:
如果我不需要更改要传递的变量,而只希望函数的解决方案的值用于打印值之类的目的,并且我不需要将答案存储在任何地方,则按值返回是有意义的!
只是想帮忙!
您的假设是有争议的(已给出评论),但对于这个问题:我建议阅读
Scott Meyers 的Effective C++(第三版),
第 21 条:当必须返回对象时,不要尝试返回引用。
示例为(稍作修改):
class Rational {
public:
Rational(int numerator = 0, int denominator = 1)
: n(numerator), d(denominator) {}
// ...
private:
int n, d;
friend Rational operator*(const Rational& lhs, const Rational& rhs);
};
使用是这样的:
Rational a(1, 2); // a = 1/2
Rational b(3, 5); // b = 3/5
Rational c = a * b; // c should be 3/10
和实施:
Rational operator*(const Rational& lhs, const Rational& rhs)
{
Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
// Would you return that local variable by reference?
// You don't want a dangling reference...
// Returning by value is the correct thing to do.
return result;
}
或更短:
Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
}
(你可以把它inline
放在标题中)。
阅读完整的项目以回顾“替代方案”(动态分配、静态变量......)以及为什么它们都是坏的(这就是重点:它们都是坏的:微妙的危险甚至完全不正确) )。
最后,从那里引用:
编写必须返回新对象的函数的正确方法是让该函数返回一个新对象。
所以:当它是自然语义时,只需按值返回。
例如,当您想要修改返回的对象而不影响调用成员函数的对象时,您可以按值返回。在以下示例中,您可以在不更改对象(及其内部b
的值)的情况下修改(由值返回):a
_val
#include<iostream>
using namespace std;
class A{
public:
A(int val) : _val(val){}
int val(){return _val;}
private:
int _val;
};
int main()
{
A a(3);
int b = a.val();
++b;
cout << "a.val() = " << a.val() << endl;
cout << "b = " << b << endl;
}
查看更多原因