首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在C++11中,常量引用什么时候比按值传递更好?

在C++11中,常量引用什么时候比按值传递更好?
EN

Stack Overflow用户
提问于 2014-07-03 08:48:25
回答 5查看 18.6K关注 0票数 58

我有一些C++11之前的代码,在这些代码中,我经常使用const引用来传递像vector这样的大参数。示例如下:

代码语言:javascript
复制
int hd(const vector<int>& a) {
   return a[0];
}

我听说有了新的C++11特性,您可以按如下方式按值传递vector,而不会影响性能。

代码语言:javascript
复制
int hd(vector<int> a) {
   return a[0];
}

例如,this answer

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

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

如果是这样,什么时候像选项1那样使用常量引用比选项2更好?(即为什么我们仍然需要在C++11中使用常量引用)。

我问的一个原因是,常量引用会使模板参数的推导变得复杂,如果在性能方面与常量引用相同,那么只使用按值传递会容易得多。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2014-07-03 08:57:37

通过值传递的一般经验规则是,当您最终创建一个副本时。这就是说,与其这样做,不如:

代码语言:javascript
复制
void f(const std::vector<int>& x) {
    std::vector<int> y(x);
    // stuff
}

在你首先传递const-ref然后复制它的地方,你应该这样做:

代码语言:javascript
复制
void f(std::vector<int> x) {
    // work with x instead
}

在C++03中,这在一定程度上是正确的,并且在移动语义中变得更加有用,因为当使用右值调用函数时,在通过val传递函数的情况下,副本可能会被移动替换。

否则,当您只想读取数据时,通过const引用传递仍然是首选的、高效的方法。

票数 75
EN

Stack Overflow用户

发布于 2014-07-03 08:52:20

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

代码语言:javascript
复制
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)

C++11和右值引用的引入改变了返回像向量这样的对象的规则--现在你可以这样做了(不用担心有保证的副本)。虽然没有改变将它们作为参数的基本规则-你仍然应该通过常量引用来获取它们,除非你真的需要一个真正的复制-按值获取。

票数 11
EN

Stack Overflow用户

发布于 2014-07-03 10:45:36

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

但是,您给出示例是通过值传递的示例

代码语言:javascript
复制
int hd(vector<int> a) {

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

即使您正确地声明了'hd‘以获取右值

代码语言:javascript
复制
int hd(vector<int>&& a) {

它可能比按值传递更便宜,但执行成功的移动(而不是简单的std::move,它可能根本没有任何效果)可能比简单的按引用传递更昂贵。必须构造一个新的vector<int>,并且它必须拥有a的内容。我们没有必须分配新的元素数组和复制值的旧开销,但我们仍然需要传输vector的数据字段。

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

代码语言:javascript
复制
std::vector<int> x;
x.push(1);
int n = hd(std::move(x));
std::cout << x.size() << '\n'; // not what it used to be

考虑下面的完整示例:

代码语言:javascript
复制
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.
}

作为一般规则:

如果目标需要可变副本,则为

  • 传递值;如果目标需要可变副本,则为
  • 传递值;如果始终需要创建副本,则为
  • 传递值;对于查看器只需查看内容/状态但不需要其可修改的非简单对象,则为
  • 传递常量引用。当目标需要临时/构造的值的可变副本时(例如,当您需要对象状态的局部性,但希望保留现有值/数据并释放当前持有者时),
  • 将移动。
票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24543330

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档