按值传递的 C++ 复制构造参数

c++ copy construct parameter passed by value

本文关键字:参数 复制 C++ 按值传递      更新时间:2023-10-16

我希望freeFunct在它自己的对象a副本上做非常量的事情。

假设 freeFunct 需要是一个自由函数因为在实际代码案例中,它需要许多不同的参数,从所有这些函数调用几个公共函数,并且没有使其成为任何类的非静态成员函数。

我想到了三种不同的声明方式。

我感觉第三种解决方案更糟。

前两者之间有什么区别吗?

还有更好的吗?

void freeFunct1(A a){
    a.doStuff(); 
}
void freeFunct2(const A& a){
    A b = a; 
    b.doStuff(); 
}
/**users of freeFunct3 are expected 
 *to give a copy of their variable: 
 *{
 *    A b = a; 
 *    freeFunct3(b); 
 *}
 */
void freeFunct3(A& a){
    a.doStuff(); 
}

第一个是最好的:它允许调用方选择是复制还是移动他的对象,因此如果调用方不需要保留副本,可以更有效率。

freeFunct1(a);             // "a" is copied and not changed
freeFunct1(std::move(a));  // "a" is moved and perhaps changed

第二个类似,但强制复制。

第三个,正如你所说,更容易出错,因为调用者必须知道它会修改参数。

首先,如前所述,如果自由函数的语义是只修改其"自己的"对象,则不要freeFunct3

其次,freeFunct1freeFunct2 之间存在差异,涉及移动优化 [C++11]、异常安全和潜在的代码大小

使用freeFunct2(通过参考常量):

  1. 它将始终构造参数的新副本,永远不会移动它 [C++11]。
  2. 如果 A 的副本构造引发异常,它将抛出函数主体内部
  3. 如果A的复制构造函数是内联的(而函数不是),它将在函数主体内部扩展一次(即使从多个不同位置调用函数)。

使用freeFunct1(按值计算):

  1. [C++11] 如果A具有移动构造函数并且传递了右值(例如调用 freeFunct1(A(args))),则可以避免复制。
  2. 如果 A 的复制(或移动)构造引发异常,它将在调用站点抛出。
  3. 如果A的复制(或移动)构造函数是内联的,它将在每个调用站点多次扩展

或者,您可以重载左值/右值引用以避免不必要地复制右值:

void freeFunct4(const A& a){
    A b = a; 
    b.doStuff(); 
}
void freeFunct4(A&& a){
    a.doStuff(); 
}

IMO,第一个是最好的,最后一个是最差的。

然而,相当多的人已经习惯了通过 const 引用传递,以至于他们默认会写 #2,即使在这种情况下他们需要它试图避免的副本。

第一个仅更改本地副本。第二个与第一个相同,但有额外的代码。第三个将更改afreeFunct3的调用方可见,因为它是非常量引用。如果按照函数上方的注释调用,那么它实际上与第二个版本没有什么不同。

因此,如果您只想修改本地副本,而不将这些更改传递给调用方,那么我推荐第一个版本。