什么是const引用比C++ 11中的pass-by-value更好?

tin*_*lyx 57 c++ c++11

我有一些pre-C++ 11代码,其中我使用const引用传递大量参数,如vector'很多.一个例子如下:

int hd(const vector<int>& a) {
   return a[0];
}
Run Code Online (Sandbox Code Playgroud)

我听说使用新的C++ 11功能,您可以vector按如下方式传递by值而不会出现性能命中.

int hd(vector<int> a) {
   return a[0];
}
Run Code Online (Sandbox Code Playgroud)

例如,这个答案

C++ 11的移动语义使得即使对于复杂对象,传递和返回值也更具吸引力.

上述两个选项在性能方面是否相同?

如果是这样,何时比选项2更好地使用const参考?(也就是为什么我们仍然需要在C++ 11中使用const引用).

我问的一个原因是const引用使模板参数的推导变得复杂,并且如果它与const引用性能相同,则仅使用pass-by-value会容易得多.

Rap*_*ptz 70

通过值传递的一般经验法则是,无论如何最终都要制作副本.也就是说,而不是这样做:

void f(const std::vector<int>& x) {
    std::vector<int> y(x);
    // stuff
}
Run Code Online (Sandbox Code Playgroud)

你首先传递一个const-ref 然后复制它,你应该这样做:

void f(std::vector<int> x) {
    // work with x instead
}
Run Code Online (Sandbox Code Playgroud)

这在C++ 03中已经部分正确,并且随着移动语义变得更加有用,因为当使用rvalue调用函数时,副本可能被pass-by-val情况中的移动所取代.

否则,当你想要做的只是读取数据时,通过const引用传递仍然是首选的有效方法.

  • 不仅通过`const`引用传递罚款,而且更受欢迎.通过价值传递给索引和元素是**强烈**禁忌. (15认同)
  • @Samuel按值传递可能会调用prvalues/xvalues上的移动构造函数和lvalues上的复制构造函数.因此,通过rvalue引用传递参数不会使您获得相同的语义. (2认同)

cub*_*l42 11

有一个很大的不同.你将获得一个vector内部数组的副本,除非它即将死亡.

int hd(vector<int> a) {
   //...
}
hd(func_returning_vector()); // internal array is "stolen" (move constructor is called)
vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
hd(v); // internal array is copied (copy constructor is called)
Run Code Online (Sandbox Code Playgroud)

C++ 11和rvalue引用的引入改变了关于返回像vector这样的对象的规则 - 现在你可以这样做(不用担心保证副本).尽管如此,没有关于它们作为参数改变的基本规则- 你仍然应该通过const引用来获取它们,除非你真的需要一个真正的副本 - 然后按值获取.


kfs*_*one 8

C++ 11的移动语义使得即使对于复杂对象,传递和返回值也更具吸引力.

但是,您提供的样本是按值传递的样本

int hd(vector<int> a) {
Run Code Online (Sandbox Code Playgroud)

所以C++ 11对此没有影响.

即使你已经正确宣布'hd'采取右值

int hd(vector<int>&& a) {
Run Code Online (Sandbox Code Playgroud)

它可能比按值传递更便宜,但执行成功的移动(与简单的std::move可能根本没有效果相反)可能比简单的传递参考更昂贵.vector<int>必须构建一个新的,它必须拥有内容的所有权a.我们没有必须分配新的元素数组并复制值的旧开销,但我们仍然需要传输数据字段vector.

更重要的是,在成功移动的情况下,a将在此过程中被销毁:

std::vector<int> x;
x.push(1);
int n = hd(std::move(x));
std::cout << x.size() << '\n'; // not what it used to be
Run Code Online (Sandbox Code Playgroud)

考虑以下完整示例:

struct Str {
    char* m_ptr;
    Str() : m_ptr(nullptr) {}
    Str(const char* ptr) : m_ptr(strdup(ptr)) {}
    Str(const Str& rhs) : m_ptr(strdup(rhs.m_ptr)) {}
    Str(Str&& rhs) {
      if (&rhs != this) {
        m_ptr = rhs.m_ptr;
        rhs.m_ptr = nullptr;
      }
    }
    ~Str() {
      if (m_ptr) {
        printf("dtor: freeing %p\n", m_ptr)
        free(m_ptr);
        m_ptr = nullptr;
      }
    }
};

void hd(Str&& str) {
  printf("str.m_ptr = %p\n", str.m_ptr);
}

int main() {
  Str a("hello world"); // duplicates 'hello world'.
  Str b(a); // creates another copy
  hd(std::move(b)); // transfers authority for b to function hd.
  //hd(b); // compile error
  printf("after hd, b.m_ptr = %p\n", b.m_ptr); // it's been moved.
}
Run Code Online (Sandbox Code Playgroud)

作为基本规则:

  • 为琐碎的物体传递价值,
  • 如果目标需要可变副本,则按值传递,
  • 如果您总是需要复制,请按值传递,
  • 通过const引用传递非平凡对象,其中查看器只需要查看内容/状态但不需要它可修改,
  • 当目的地需要临时/构造值的可变副本时移动(例如std::move(std::string("a") + std::string("b"))),
  • 当您需要对象状态的位置但希望保留现有值/数据并释放当前持有者时移动.

  • 你为什么在规则#5中使用`std :: move(... + ...)`?它没有伤害,但你不需要`std :: move()`连接的结果. (3认同)

zda*_*dan 7

请记住,如果您没有传入r值,那么按值传递将导致完整的复制.所以一般来说,通过价值传递可能会导致性能下降.