当用于初始化另一个对象时,为什么要按值传递参数
Why should arguments be passed by value when used to initialize another object?
向函数传递对象时,可以选择按值或按const&
传递参数。特别是当对象的创建成本可能很高,并且它在内部发生了变异或用于初始化另一个对象时,建议按值传递对象。例如:
class Foo {
std::vector<std::string> d_strings;
public:
Foo(std::vector<std::string> strings): d_strings(std::move(strings)) {}
// ...
};
传统的方法是将strings
参数声明为std::vector<std::string> const&
并复制该参数。上面构造函数的值参数也需要复制!
为什么按值传递比按const&
传递更可取?
当const&
传递strings
参数时,有一个有保证的副本:除了复制它,没有其他方法可以获得参数的副本。问题变成了:传递值时有什么不同?
当参数通过值传递时,strings
对象显然不在其他地方使用,并且其内容可以移动。移动构造扩展到复制对象可能仍然相对便宜。例如,在std::vector<std::string>
的情况下,移动只是将几个指针复制到新对象,并设置几个指针来指示原始对象不应该释放任何内容。
不过,仍然有必要创建这个论点。但是,在不创建新对象的情况下,可能会取消参数的创建。例如
Foo f(std::vector<std::string>{ "one", "two", "three" });
将创建一个具有三个字符串的向量,并且Foo
构造的参数的构造很可能被省略。在最坏的情况下,参数是通过移动临时向量来构建的,同时也避免了复制。
当然,在某些情况下,仍然需要创建副本。例如,在的情况下
std::vector<std::string> v{ "one", "two", "three" };
Foo f(v);
参数由副本创建。然后将准备好的副本移动到成员。在这种情况下,传递const&
会更好,因为只需要复制构造,而不需要复制构造(创建参数),然后再进行移动构造(创建成员)。
也就是说,通过传递值可以完全消除副本,只进行移动。在最坏的情况下,当无论如何都需要复制参数时,需要执行额外的移动。由于移动通常被认为是一种廉价的操作,因此需要转移的对象的总体传递值会带来更好的性能。
语句
用于初始化另一个对象时,应按值传递参数
是的,从C++11开始,这要归功于移动语义的引入。它可以概括为:
当函数需要其某个参数的副本时,请按值传递。
这一点在"想要速度?通过价值传递"一文中有很好的阐述。
要点是,由于函数无论如何都需要参数的副本,因此最好在调用站点处理此副本,而不是在被调用函数内部处理。这是因为,如果函数需要复制的对象是一个Rvalue,那么它只在调用站点是已知的,从而实现移动优化:调用上下文很清楚对象即将过期,因此可以将其移动到函数需要的副本中。现在,如果复制是在函数本身内部进行的,那么源对象(在调用上下文中)是Rvalue的概念将不会转发到复制的实际位置,从而失去移动的机会。
- 何时应通过引用传递矢量参数而不是按值传递矢量参数?
- C++类 - 初始化列表 - 递归 - 按值传递
- 为什么按值传递QStringView比引用常量更快?
- 为什么在按值返回时创建临时对象,而不是在按值传递给函数参数时创建临时对象
- 为什么要按值传递string_view?为什么Visual Studio不能优化这一点?
- 为什么C++编译器不在按值传递方案中优化更多的字符串构造?
- 为什么 std::vector<int> 内容在按值传递时被消除?
- 为什么功能程序的规律性允许按值传递和按常量引用传递?
- 为什么按值传递 int 比按引用传递更快
- 为什么在按值传递指针之后再按引用传递指针会指向原始内存位置
- 为什么我必须声明这些引用参数常量或按值传递
- 为什么 GCC 的 std::function 不使用对按值传递的参数的右值引用来在其内部委托之间传递它们?
- 为什么gcc在按值传递琐碎结构时会发出不必要的内存访问
- 当用于初始化另一个对象时,为什么要按值传递参数
- 为什么在按值传递时不调用构造函数
- 为什么向量被视为按值传递,即使它是通过常量引用传递的
- 为什么不允许复制构造函数按值传递
- 如果函数参数类型是ABC,为什么不能按值传递?
- 为什么std::vector::resize(n, src)按值传递?
- 为什么即使对象没有复制构造函数,也可以按值传递对象