C++ 为什么按值传递而不是按常量引用传递?
因为C++ 为什么按值传递而不是按常量引用传递?,c++,function,arguments,constants,C++,Function,Arguments,Constants,因为const引用与通过值传递几乎相同,但没有创建副本(据我所知)。所以有一种情况需要创建变量的副本(所以我们需要使用传递值) 按值传递基本数据类型(如int、float和指针)通常更快 函数可能希望在本地修改参数,而不改变传入变量的状态 C++11引入了移动语义。若要将对象移动到函数参数中,其类型不能为常量引用 在某些情况下,您不修改输入,但仍然需要输入的内部副本,然后还可以按值获取参数。例如,假设有一个函数返回向量的排序副本: template <typename V> V so
const
引用与通过值传递几乎相同,但没有创建副本(据我所知)。所以有一种情况需要创建变量的副本(所以我们需要使用传递值)
在某些情况下,您不修改输入,但仍然需要输入的内部副本,然后还可以按值获取参数。例如,假设有一个函数返回向量的排序副本:
template <typename V> V sorted_copy_1(V const & v)
{
V v_copy = v;
std::sort(v_copy.begin(), v_copy.end());
return v;
}
template V sorted_copy_1(V const&V)
{
V_copy=V;
排序(v_copy.begin(),v_copy.end());
返回v;
}
这很好,但是如果用户有一个向量,他们永远不需要用于任何其他目的,那么您必须在这里创建一个可能不必要的强制副本。因此,只需按值获取参数:
template <typename V> V sorted_copy_2(V v)
{
std::sort(v.begin(), v.end());
return v;
}
模板V已排序\u复制\u 2(V)
{
排序(v.begin(),v.end());
返回v;
}
现在,生成、排序和返回向量的整个过程基本上都可以“就地”完成
较便宜的例子是使用计数器或迭代器的算法,这些计数器或迭代器需要在算法过程中进行修改。同样,通过值获取这些参数可以直接使用函数参数,而不需要本地副本。最好的例子可能是复制和交换习惯用法:
C& operator=(C other)
{
swap(*this, other);
return *this;
}
通过值而不是常量引用获取other
,可以更轻松地编写正确的赋值运算符,从而避免代码重复并提供强大的异常保证
此外,通过值传递迭代器和指针,因为这样可以使这些算法更合理地编码,因为它们可以在本地修改参数。否则,像这样的东西无论如何都必须立即复制它的输入,这既低效又愚蠢。我们都知道,避免使用看似愚蠢的代码是首要任务:
template<class BidirIt, class UnaryPredicate>
BidirIt partition(BidirIt first, BidirIt last, UnaryPredicate p)
{
while (1) {
while ((first != last) && p(*first)) {
++first;
}
if (first == last--) break;
while ((first != last) && !p(*last)) {
--last;
}
if (first == last) break;
std::iter_swap(first++, last);
}
return first;
}
模板
BidirIt分区(BidirIt first,BidirIt last,一元谓词p)
{
而(1){
而((第一个!=最后一个)和&p(*第一个)){
++第一,;
}
如果(第一次==最后一次--)中断;
while((first!=last)和&!p(*last)){
--最后;
}
如果(第一次==最后一次)中断;
标准:国际热核聚变实验堆交换(第一次++,最后一次);
}
先返回;
}
Aconst&
如果没有const\u cast
通过引用,则无法更改,但可以更改。在代码离开编译器“分析范围”的任何时候(可能是对不同编译单元的函数调用,或者通过函数指针,它无法在编译时确定的值),必须假定引用的值可能已更改
这需要优化成本。而且,这会使您更难对代码中可能存在的bug或怪癖进行推理:引用是非本地状态,而只在本地状态下运行且不会产生副作用的函数很容易推理。使您的代码易于推理是一大好处:维护和修复代码所花费的时间比编写代码所花费的时间要多,而且在性能上所花费的精力是可以替代的(您可以将其花在重要的地方,而不是在任何地方的微优化上浪费时间)
另一方面,一个值需要将该值复制到本地自动存储中,这是有成本的
但是,如果您的对象复制成本很低,并且您不希望出现上述效果,请始终按值执行,因为这会使编译器更容易理解函数
当然,只有在复制价值便宜的情况下。如果复制成本很高,或者即使复制成本未知,该成本应该足以由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
将创建一个std::string
,其中包含“Hello world!”
。然后将其再次复制到某些外部\u状态
。复制2份,丢弃1个字符串
对bar
的调用直接创建std::string
参数。然后将其状态移动到某些外部状态
。创建1个副本,移动1个,丢弃1个(空)字符串
由于任何分配都发生在bar
之外,而foo
可能引发资源耗尽的异常,因此该技术还带来了某些异常安全改进
这仅适用于完美转发令人讨厌或失败、移动成本低、复制成本高以及几乎肯定要制作参数的本地副本的情况
最后,还有一些小类型(如
int
),直接拷贝的非优化ABI比常量和参数的非优化ABI快。当编码不能或不会优化的接口时,这一点很重要,通常是一种微观优化。像许多事情一样,这是一种平衡
我们通过常量引用传递给