可能重复:
Is it better in C++ to pass by value or pass by constant reference?
我知道在C ++中传递值,指针和引用的区别,我会考虑在C ++中按值(而不是const引用)传递对象几乎总是编程错误。
void foo(Obj o); ... // Bad
void foo(const Obj &o); ... // Better
我能想到的唯一情况是,通过值而不是const引用传递的位置是对象小于引用的位置,因此传递值更有效。
但是,这肯定是编译器构建来确定的那种东西吗?
为什么C ++实际上需要通过值传递并通过const引用,并且 - 编译器是否允许自动将调用转换为(和来自)const引用(如果合适)?
(似乎有100个C ++调用约定问题,询问(说)值和引用之间的差异 - 但我找不到一个问“为什么?”。) < / p>
答案 0 :(得分:10)
通过值传递的时间可能比使用const引用更好的问题对于不同版本的标准有不同的答案。
在好的旧C ++ 03和几年前,建议通过const引用传递任何不适合寄存器的东西。在这种情况下,答案是:
Obj
适合寄存器并且按值传递并且传递值会更有效仍然在C ++ 03中,在过去的几年里(看起来很荒谬,因为看起来有些文章推荐这差不多10年了,但是没有真正的共识),
批准新的C ++ 11标准,并增加对 rvalue-references 的编译器支持,在许多情况下即使不能省略副本,也会再次
至于为什么两种不同的召集惯例,它们有不同的目标。按值传递允许函数修改参数的状态而不会干扰源对象。此外,源对象的状态也不会干扰该函数(考虑多线程环境,以及在函数仍在执行时修改源的线程)。
答案 1 :(得分:6)
当然,C ++具有pass-by-value的一个原因是因为它从C继承了它,并且删除它可能会破坏代码以获得很少的收益。
其次,正如您所注意到的,对于小于参考值的类型,传递值的效率会降低。
另一个不那么明显的情况是,如果你有一个函数需要它的参数的副本出于某种原因:
void foo(const Obj& obj)
{
if(very_rare_check()) return;
Obj obj_copy(obj);
obj_copy.do_work();
}
在这种情况下请注意您正在强制复制。但是假设您使用另一个按值返回的函数的结果调用此函数:
Obj bar() { return Obj(parameters); }
如此称呼它:foo(bar());
现在,当您使用const引用版本时,编译器最终会生成两个对象:临时文件和foo
中的副本。但是,如果按值传递,编译器可以将所有临时值优化到foo
的by-value参数所使用的位置。
有关于此的精彩文章,并在http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
处移动语义最后,实现某些运算符的规范方法是使用pass-by-value来避免运算符内的副本:
Obj operator+(Obj left, const Obj& right)
{
return left += right;
}
注意这是如何让编译器在参数中生成副本,而不是在操作符的代码中强制复制或临时对象。
答案 2 :(得分:4)
如果我想在不影响原件的情况下对函数内的对象执行操作,我会按值传递:
A minus(A b){
b.val=-b.val;
return b;
}
答案 3 :(得分:3)
复制交换习惯用法使用passe by value来实现编译器生成的副本。
MyClass& operator=(MyClass value) // pass by value to generate copy
{
value.swap(*this); // Now do the swap part.
return *this;
}
基本上在需要修改参数但不想触摸原件的情况下。在这些情况下,如果通过const引用,则需要手动在函数内创建副本。如果让编译器处理副本,此手动步骤将阻止编译器可以执行的某些优化。
MyClass a;
// Some code
a = MyClass(); // reset the value of a
// compiler can easily elide this copy.
答案 4 :(得分:1)
如果对象是可变的,那么传递值会给接收者自己的副本以及合理的变化,而不会影响调用者的副本 - 总是假设它是一个足够深的副本。
这可以简化一些多线程情况下的思考。
答案 5 :(得分:1)
为什么C ++实际上需要传递值并通过const引用传递,并且 - 编译器是否允许自动将调用转换为(和)const引用(如果合适)?
让我先回答第二个问题:有时候。
允许编译器将副本删除到参数中,但仅限于传入rvalue临时值。例如:
void foo(Obj o);
foo((Obj()))); //Extra set of parenthesis are needed to prevent Most Vexing Parse
在编译方便的时候,可以省略将临时复制到参数参数中(即:不复制)。
但是,此副本永远将被忽略:
Obj a;
foo(a);
现在,第一个。 C ++需要两者,因为您可能希望将两者用于不同的事情。通过价值转移对转让所有权很有用;这在C ++ 11中更为重要,我们可以移动而不是复制对象。