如果没有const_cast,则无法通过引用更改const&,但可以更改它。在代码离开编译器的“分析范围”的任何时候(可能是对不同编译单元的函数调用,或者通过函数指针无法确定编译时的值),它必须假定所引用的值可能具有改变了。
这种成本优化。而且它可能使您更难以推理代码中可能存在的错误或怪癖:引用是非本地状态,仅在本地状态上运行且不产生副作用的函数非常容易推理关于。让你的代码易于推理是一个很大的好处:维护和修复代码比编写代码花费更多的时间,而且花在性能上的努力是可替代的(你可以把它花在重要的地方,而不是把时间浪费在任何地方的微优化上)。
另一方面,一个值需要将该值复制到本地自动存储中,这是有成本的。
但是,如果您的对象复制起来很便宜,并且您不希望出现上述效果,请始终按值取值,因为它使编译器更容易理解函数。
当然,只有在复制成本低的情况下。如果复制成本很高,或者即使复制成本未知,那么 const& 的成本应该足以承担。
上述的简短版本:取值使您和编译器更容易推理参数的状态。
还有一个原因。如果您的对象移动起来很便宜,并且无论如何您都将存储本地副本,那么按价值获取会提高效率。如果您通过const& 获取std::string,然后制作本地副本,则可以创建一个std::string 以传递这些参数,并为本地副本创建另一个。
如果您按值获取std::string,则只会创建一个副本(并且可能会移动)。
举个具体的例子:
std::string some_external_state;
void foo( std::string const& str ) {
some_external_state = str;
}
void bar( std::string str ) {
some_external_state = std::move(str);
}
然后我们可以比较:
int main() {
foo("Hello world!");
bar("Goodbye cruel world.");
}
对foo 的调用会创建一个包含"Hello world!" 的std::string。然后将其再次复制到some_external_state。制作了 2 个副本,丢弃了 1 个字符串。
对bar 的调用直接创建std::string 参数。然后将其状态移至some_external_state。创建 1 个副本,1 个移动,丢弃 1 个(空)字符串。
此技术还带来了某些异常安全性改进,因为任何分配都发生在 bar 之外,而 foo 可能会引发资源耗尽异常。
这仅适用于完美转发烦人或失败、移动成本低廉、复制成本高昂以及您几乎肯定会制作参数的本地副本的情况。
最后,有一些小类型(如int),直接复制的非优化ABI比const&参数的非优化ABI更快。这主要在编码无法或不会优化的接口时很重要,通常是微优化。