我有一些pre-C ++ 11代码,其中我使用const
引用来传递大量参数,例如vector
&#39}。一个例子如下:
int hd(const vector<int>& a) {
return a[0];
}
我听说使用新的C ++ 11功能,您可以按照以下方式传递vector
,而不会影响性能。
int hd(vector<int> a) {
return a[0];
}
例如,this answer说
C ++ 11的移动语义使得传递和返回值更具吸引力,即使对于复杂的对象也是如此。
上述两个选项在性能方面是否相同?
如果是这样,何时使用const引用,如选项1中的选项2更好? (即为什么我们仍然需要在C ++ 11中使用const引用)。
我问的一个原因是const引用使模板参数的推导变得复杂,并且如果它与const引用性能相同,那么仅使用pass-by-value会容易得多。
答案 0 :(得分:69)
传递值的一般经验法则是,无论如何最终都要制作副本。也就是说,而不是这样做:
void f(const std::vector<int>& x) {
std::vector<int> y(x);
// stuff
}
你首先传递一个const-ref然后然后复制它,你应该这样做:
void f(std::vector<int> x) {
// work with x instead
}
这在C ++ 03中已经部分正确,并且随着移动语义变得更加有用,因为复制可能被替换为在pass-by-val情况下的移动用右值调用。
否则,当您只想读取数据时,通过const
引用仍然是首选的有效方法。
答案 1 :(得分: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)
C ++ 11和rvalue引用的引入改变了关于返回像vector这样的对象的规则 - 现在你可以做到(不用担心保证副本)。虽然没有关于将作为参数的基本规则改变了 - 你仍然应该通过const引用来接受它们,除非你真的需要一个真正的副本 - 然后按值获取。
答案 2 :(得分:7)
请记住,如果您没有传入r值,那么按值传递将导致完整的复制。所以一般来说,按值传递可能会导致性能下降。
答案 3 :(得分:7)
C ++ 11的移动语义使得传递和返回值更具吸引力,即使对于复杂的对象也是如此。
但是,您提供的样本是按值传递的样本
int hd(vector<int> a) {
所以C ++ 11对此没有影响。
即使您已正确宣布&#39; hd&#39;采取右值
int hd(vector<int>&& a) {
它可能比按值传递更便宜,但执行成功移动(而不是简单的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
考虑以下完整示例:
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.
}
作为一般规则:
std::move(std::string("a") + std::string("b")))
。答案 4 :(得分:3)
你的榜样有缺陷。 C ++ 11不会让您使用您拥有的代码,并且会进行复制。
但是,您可以通过声明函数采用右值引用然后传递一个来获得移动:
int hd(vector<int>&& a) {
return a[0];
}
// ...
std::vector<int> a = ...
int x = hd(std::move(a));
假设您不会再次使用函数中的变量a
,除非要销毁它或为其赋值新值。在这里,std::move
将值转换为右值引用,允许移动。
Const引用允许以静默方式创建临时对象。您可以传入适合隐式构造函数的内容,并创建临时构造函数。经典示例是将char数组转换为const std::string&
但使用std::vector
,可以转换std::initializer_list
。
所以:
int hd(const std::vector<int>&); // Declaration of const reference function
int x = hd({1,2,3,4});
当然,你也可以移动临时版:
int hd(std::vector<int>&&); // Declaration of rvalue reference function
int x = hd({1,2,3,4});