[英]const parameter vs const reference parameter
实施1:
foo(const Bar x);
实施2:
foo(const Bar & x);
如果在函数内不更改对象,为什么要复制它(实现1)。
这将由编译器自动优化吗?
摘要 :即使对象被声明为const
函数声明,它仍然是有可能的对象通过一些其他的别名进行编辑&
。
如果您是编写库的人并且知道您的函数不这样做或者该对象足够大以证明每次操作的解除引用成本,那么foo(const Bar & x);
是要走的路。
第2部分 :
这将由编译器自动优化吗?
既然我们确定它们并不总是等价的,并且等价的条件是非平凡的,那么编译器通常很难确保它们,所以几乎肯定 没有
实际上,在这种情况下,您通常会使用方法2。
通常情况下,如果对象很小,您只会使用方法1,因此复制它一次比通过引用重复访问它更便宜(这也会产生成本)。 在TC ++ PL中 ,Stroustrup开发了一个复数类,并将其传递给它,正是出于这个原因。
你问,
“如果在函数中不改变对象,为什么要复制它(实现1)。”
有一些奇怪的情况,通过引用传递的对象可能会被其他代码更改,例如
namespace g { int x = 666; }
void bar( int ) { g::x = 0; }
int foo( int const& a ) { assert( a != 0 ); bar( a ); return 1000/a; } // Oops
int main() { foo( g::x ); }
从20世纪90年代中期开始,这种情况从未发生在我身上。
所以,这种混叠是该类型的单个参数的理论问题 。
有两个相同类型的参数,它更有可能。 例如,赋值运算符可能会传递它被调用的对象。 当参数通过值传递时(如交换习语的最小形式),这没有问题,但如果不是,则通常需要避免自我分配 。
你进一步问,
“这会被编译器自动优化吗?”
不,一般情况下,由于上述原因
编译器通常不能保证参考参数不会有别名(但有一个例外,就是内联调用的机器代码)
然而,在第三手,语言可以想象都支持这个编译器,例如,通过提供程序员的方式来明确接受任何这样的优化,比如,一种方法,说:“这个代码是安全的,通过更换合格优化通过引用传递的值,请继续,编译器“
它可能在某些情况下得到优化,但有很多东西可以阻止它。 在以下情况下,编译器无法避免复制:
x
的地址或对它的引用,并将它传递给一些可能能够将它与原始地址进行比较的代码。 foo
运行时对象可能会改变,例如因为foo
调用了一些其他改变它的函数。 我不确定这是否是你的意思是通过说“对象不会在函数内被改变”来排除,但如果没有那么它就在游戏中。 如果任何这些事情对您的程序很重要,您可以复制它:
如果你认为副本效率更高,你也会复制它,通常认为是像“ int
”这样的“小”类型的情况。 标准算法中的迭代器和谓词也是有价值的。
最后,如果您的代码计划无论如何都要复制对象(包括通过分配给现有对象),那么合理的习惯用法就是首先将副本作为参数。 然后从参数中移动/交换。
如果对象从其他地方改变了怎么办?
void f(const SomeType& s);
void g(const SomeType s);
int main() {
SomeType s;
std::thread([&](){ /* s is non-const here, and we can modify it */}
// we get a const reference to the object which we see as const,
// but others might not. So they can modify it.
f(s);
// we get a const *copy* of the object,
// so what anyone else might do to the original doesn't matter
g(s);
}
如果对象是const,但有可变成员怎么办? 然后你仍然可以修改对象,因此无论你是否拥有原件的副本或引用都非常重要。
如果对象包含指向另一个对象的指针怎么办? 如果s
是常量,指针将是常量, 但不受的常量性指向什么s
。 但是创建一个副本将(希望)给我们一个深层副本,所以我们得到了我们自己的(const)对象,其中一个单独的(const)指针指向一个单独的(非const)对象。
在许多情况下,const副本与const引用不同。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.