当用于初始化另一个对象时,为什么要通过值传递参数?

时间:2015-11-24 10:20:07

标签: c++ c++11

将对象传递给函数时,可以选择按值或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&并复制参数。上面的构造函数的value参数也需要复制!

为什么优先传递值而不是传递const&

2 个答案:

答案 0 :(得分:4)

当通过strings传递const&参数时,有一个保证副本:除了复制它之外,没有其他方法可以保存参数的副本。问题变成:通过价值时有什么不同?

当参数按值传递时,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&会更好,因为只需要复制构造而不是复制构造(创建参数),然后是移动构造(创建成员)。

也就是说,通过值传递可以完全删除副本并且只是移动。在最坏的情况下,无论如何需要复制参数时,需要执行额外的移动。由于移动通常被认为是一种廉价的操作,因此期望的是,需要转移的对象的整体价值转移会带来更好的性能。

答案 1 :(得分:3)

声明

  

当用于初始化另一个对象时,应该通过值传递参数

是的,从C ++ 11开始,归功于移动语义的引入。 它可以概括为:

  

当函数需要其中一个参数的副本时,请按值传递。

这在“Want Speed? Pass by Value.”文章中非常详细。

大纲是,因为你的函数无论如何都需要参数的副本,所以最好在调用站点而不是在被调用函数内部处理这个副本。这是因为,如果函数需要副本的对象是Rvalue,它只在调用站点知道,从而启用移动优化:调用上下文很清楚对象即将到期,因此可以将其移动到复制该功能所需。现在,如果副本是在函数本身内部进行的,那么源对象(在调用上下文中)是Rvalue的概念将不会被转发到副本的实际位置,从而失去了移动的机会。 / p>