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

C++ 当用于初始化另一个对象时,为什么参数要按值传递?,c++,c++11,C++,C++11,将对象传递给函数时,可以选择按值传递参数,也可以选择按常量传递参数。尤其是当对象的创建成本可能很高,并且它在内部发生了变化或用于初始化另一个对象时,建议按值传递对象。例如: class Foo { std::vector<std::string> d_strings; public: Foo(std::vector<std::string> strings): d_strings(std::move(strings)) {} // ... };

将对象传递给函数时,可以选择按值传递参数,也可以选择按常量传递参数。尤其是当对象的创建成本可能很高,并且它在内部发生了变化或用于初始化另一个对象时,建议按值传递对象。例如:

class Foo {
    std::vector<std::string> d_strings;
public:
    Foo(std::vector<std::string> strings): d_strings(std::move(strings)) {}
    // ...
};
class-Foo{
std::向量d_字符串;
公众:
Foo(std::vector strings):d_strings(std::move(strings)){}
// ...
};
传统的方法是将
字符串
参数声明为
std::vector const&
并复制参数。上面构造函数的value参数也需要复制


为什么按值传递比按常量传递更可取

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

当参数通过值传递时,
字符串
对象显然不在其他地方使用,其内容可以移动。将扩展对象移动到复制对象的构造可能仍然相对便宜。例如,在
std::vector
的情况下,移动只是将一些指针复制到新对象,并设置一些指针以指示原始对象不应释放任何内容

不过,仍然需要创建参数。但是,在不创建新对象的情况下,可以省略参数的创建。比如说

Foo f(std::vector<std::string>{ "one", "two", "three" });
参数是由副本创建的。然后将准备好的副本移动到成员。在这种情况下,pass-by
const&
会更好,因为只需要一个复制构造,而不是一个复制构造(创建参数),然后是一个移动构造(创建成员)

也就是说,通过值传递可以完全删除副本,而只进行移动。在最坏的情况下,当仍然需要复制参数时,需要执行额外的移动。由于移动通常被认为是一种廉价的操作,因此期望需要传输的对象的整体传递值能够产生更好的性能。

该语句

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

这是真的,从C++11开始,多亏了移动语义的引入。 可以概括为:

当函数需要一个参数的副本时,按值传递它

这实际上在“”文章中有详细介绍


要点是,由于您的函数无论如何都需要参数的副本,因此最好在调用站点而不是在被调用函数中处理此副本。这是因为,如果函数需要一个副本的对象是一个右值,那么它只在调用站点已知,从而启用移动优化:调用上下文非常清楚该对象即将过期,因此可以将其移动到函数需要的副本中。现在,如果复制是在函数本身内部进行的,那么源对象(在调用上下文中)是右值的概念将不会被转发到复制的实际位置,从而失去了移动的机会。

通过值传递是“建议”,这可能是一种有点大胆的说法我认为这绝对是叶代码的好建议,因为它易于教授,避免了复杂性,用户不需要拼写像
&&
这样的笨拙标点符号。但是,在通用库代码中,似乎更适合使用两个重载(
const string&
string&&
),以避免强制复制/移动(尽管可能很便宜)。@KerrekSB:创建一个函数通常比创建两个函数更不容易出错。我同意一个高质量的实现可能需要创建两个重载,尽管
const&
版本实际上只是避免在实际需要复制时移动。决策中的另一个重要决定因素是您是否知道具体的参数类型,或者您是否有泛型代码。在前一种情况下,你可以对额外搬迁的成本更有信心。对我来说,区别不在于QoI,而在于代码的通用性和重用程度。我对我的用户了解得越少,我就越不想约束他们。
move
并不便宜:move
std::array
无论如何都会进行复制,所以通过常量引用传递它。如果我的类包含一个类型为
std::array
的数据成员,该成员将用这种类型的现有对象的值初始化,该怎么办?这绝对是“需要一份其论点的副本”。你在cppcon2014上见过草药公司谈论这一点吗?你对此有何看法?就个人而言,我会给出一个更为严格的一般性建议:“如果必须复制(而不是指定)内部的对象(当然还有小型/内置类型),请仅使用传递值”。
std::vector<std::string> v{ "one", "two", "three" };
Foo                      f(v);