【问题标题】:C++ lvalues, rvalues, references, parameters, and performanceC++ 左值、右值、引用、参数和性能
【发布时间】:2015-09-27 17:46:51
【问题描述】:

所以我有一个需要将 std::vector 作为参数的函数。我想知道声明参数的最佳方式,这样底层数组就不会被深度复制,因为它可能相当大。

// which should I choose?
void myFunc(std::vector<char>); // 1
void myFunc(std::vector<char>&); // 2
void myFunc(std::vector<char>&&);  // 3
void myFunc(std::vector<char>*) // 4

我应该选择哪个?另外,我不会修改函数中的向量,所以我不应该添加 const 吗?我应该重载函数并结合这些吗?

【问题讨论】:

  • 如果不需要修改,那么const std::vector&lt;char&gt;&amp;
  • 这对右值有用吗?
  • 另外,可能你忘了cv-qualifiers...

标签: c++ c++11 parameters lvalue rvalue


【解决方案1】:
  1. 如果你还是要在函数中复制它:

    void myFunc(std::vector<char>);
    
  2. 如果您只想阅读参数而不复制它:

    void myFunc(const std::vector<char>&);
    
  3. 如果要修改传递给函数的原始向量:

    void myFunc(std::vector<char>&);
    
  4. 如果你想针对右值进行优化或者如果你想move 参数进入函数:

    void myFunc(std::vector<char>&&);
    
  5. 如果您需要能够表示通过引用传递的可选参数:

    void myFunc(const std::vector<char>*);
    
  6. 如果需要传递一个非nullptr时要修改的可选参数:

    void myFunc(std::vector<char>*);
    

【讨论】:

  • 根据这个答案stackoverflow.com/questions/6368690/rvalue-function-overloading 选项1将针对右值进行优化......所以我有点困惑。
  • @dwoodwardgb 是的。这正是它应该被使用的原因。您将获得一个有效处理左值和右值的函数。
  • 好的,但是选项 1 如何用于左值?它不会调用 std::vector 的复制构造函数吗?
  • 是的,这就是为什么你只在要复制函数内部的向量时才使用它的原因。
  • 在函数内复制数组会如何改变?也就是说,如果我要在函数内复制向量,为什么还要在函数调用中复制它?
【解决方案2】:

如果你不想想要深拷贝std::vector:

  • 移动到你的函数中:

    // foo() decalaration
    void foo(std::vector<int> v);
    
    // usage example
    std::vector<int> v {0, 1, 2, 3};
    foo(std::move(v));
    // v is moved into foo() and invalid now
    

    你也可以用同样的方式从函数中返回这个向量:

    // foo() decalaration
    std::vector<int> foo(std::vector<int> v) {
        return v.push_back(4), std::move(v);
    }
    
    // usage example
    std::vector<int> v {0, 1, 2, 3};
    v = foo(std::move(v));
    // now v is {0, 1, 2, 3, 4} and no one were deep copied
    

    但请注意,如果您不移动它(调用foo(v) 而不是foo(std::move(v))),那么它将被深度复制。在后台,foo() 的参数 v 只是由 move-constructor 构造的。

  • 作为参考传递:

    // foo() declaration
    void foo(std::vector<int>& v);
    

    但是现在我们有一个问题:哪个引用和cv-qualifiers?嗯,一般我们有2种引用和4种cv-qualifiers,一共8个声明:

    void foo(std::vector<int>&);
    void foo(std::vector<int> const&);
    void foo(std::vector<int> volatile&);
    void foo(std::vector<int> const volatile&);
    void foo(std::vector<int>&&);
    void foo(std::vector<int> const&&);
    void foo(std::vector<int> volatile&&);
    void foo(std::vector<int> const volatile&&);
    

    当然,其中一部分是无用的,应该删除。但尽管如此,过多的声明也被称为 完美转发问题(实际上,当出现问题时没有右值引用,因此问题小了 2 倍)。

    例如,如果要修改v,则至少需要2个函数:

    void foo(std::vector<int>&);
    void foo(std::vector<int>&&);
    

    在这种情况下,您将能够在左值对象上调用 foo()

    std::vector<int> v;
    foo(v);
    

    以及临时:

    foo(std::vector<int>{1, 2, 3, 4, 5});
    

    但是如何编码只针对不同的引用类型和/或 cv 限定符的一种实现呢?让我介绍一下通用参考

    template<typename Vector>
    void foo(Vector&& v);
    

    Vector&amp;&amp;始终是引用类型,可以推导出为

    • std::vector&lt;int&gt;&amp; 如果你将std::vector&lt;int&gt; 类型的左值传递给foo()

      std::vector<int> v; 
      foo(v); // v is lvalue
      
    • std::vector&lt;int&gt; const&amp; 如果你传递 std::vector&lt;int&gt; 类型的 const 左值:

      std::vector<int> const v; 
      foo(v); // v is const lvalue
      
    • std::vector&lt;int&gt;&amp;&amp; 如果你传递右值:

      foo(std::vector<int>{0, 1, 2}); // v is rvalue
      
    • 等等……

    但在这种情况下,您必须检查 Vector 类型的接受度。但那是另一回事了。

在这种情况下,我绝对认为传递指针而不是引用毫无意义。

【讨论】:

  • 感谢您的详尽回答!我不知道整个通用引用的事情。我最终只是简单地使用了 myFunc(const std::vector &) 因为我根本不需要修改向量。
  • @dwoodwardgb 是的,这取决于目的。此外,auto&amp;&amp; 也是通用引用。
猜你喜欢
  • 1970-01-01
  • 2017-12-03
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-30
  • 1970-01-01
相关资源
最近更新 更多