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 吗?我应该重载函数并结合使用这些函数吗?
如果您无论如何都要在函数中复制它:
void myFunc(std::vector<char>);
如果您只想阅读参数而不复制它:
void myFunc(const std::vector<char>&);
如果要修改传递给函数的原始向量:
void myFunc(std::vector<char>&);
如果你想优化右值或如果你想move函数中的参数:
void myFunc(std::vector<char>&&);
如果您需要能够表示通过引用传递的可选参数:
void myFunc(const std::vector<char>*);
如果需要传递一个非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
。但那是另外一回事了。
在这种情况下,我绝对认为传递指针而不是引用毫无意义。
所以我有一个函数需要将 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 吗?我应该重载函数并结合使用这些函数吗?
如果您无论如何都要在函数中复制它:
void myFunc(std::vector<char>);
如果您只想阅读参数而不复制它:
void myFunc(const std::vector<char>&);
如果要修改传递给函数的原始向量:
void myFunc(std::vector<char>&);
如果你想优化右值或如果你想move函数中的参数:
void myFunc(std::vector<char>&&);
如果您需要能够表示通过引用传递的可选参数:
void myFunc(const std::vector<char>*);
如果需要传递一个非
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
。但那是另外一回事了。
在这种情况下,我绝对认为传递指针而不是引用毫无意义。