C++ 左值、右值、引用、参数和性能

C++ lvalues, rvalues, references, parameters, and performance

所以我有一个函数需要将 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 吗?我应该重载函数并结合使用这些函数吗?

  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>*);
    

如果你不想深度复制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
    

    您也可以return这个向量以同样的方式运行:

    // 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&& 始终是引用类型 并且可以推导为

    • std::vector<int>& 如果将 std::vector<int> 类型的左值传递给 foo():

      std::vector<int> v; 
      foo(v); // v is lvalue
      
    • std::vector<int> const& 如果传递 std::vector<int>:

      类型的常量左值
      std::vector<int> const v; 
      foo(v); // v is const lvalue
      
    • std::vector<int>&& 如果传递右值:

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

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

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