在c++ 11中,When是比按值传递更好的const引用
When is a const reference better than pass-by-value in C++11?
我有一些c++ 11之前的代码,其中我使用const
引用来传递像vector
这样的大参数。示例如下:
int hd(const vector<int>& a) {
return a[0];
}
我听说在c++ 11的新特性中,您可以按以下值传递vector
而不会影响性能。
int hd(vector<int> a) {
return a[0];
}
例如,这个答案说
c++ 11的move语义使得传递和按值返回即使对于复杂的对象也更有吸引力。
以上两个选项是否在性能方面是相同的?
如果是,什么时候使用const引用选项1比选项2好?(例如,为什么c++ 11中仍然需要使用const引用)。
我问这个问题的一个原因是const引用使模板形参的推导变得复杂,如果在性能方面与const引用相同,那么只使用按值传递会容易得多。
按值传递的一般经验规则是当您最终将生成副本时。也就是说,与其这样做:
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中部分是正确的,并且在move语义中变得更加有用,因为当用右值调用函数时,复制可以用move代替。
否则,当您想要做的只是读取数据时,通过const
引用传递仍然是首选的,有效的方法。
差别很大。您将获得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和右值引用的引入改变了关于返回对象(如vector)的规则——现在可以这样做(而不用担心保证复制)。关于将作为参数的基本规则没有改变——你仍然应该通过const引用接受它们,除非你真的需要一个真正的按值复制。
c++ 11的move语义使得传递和按值返回即使对于复杂的对象也更有吸引力。
然而,你给出的样本是一个通过值
的样本int hd(vector<int> a) {
所以c++ 11对这个没有影响。
即使你正确地声明了'hd'接受右值
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 %pn", m_ptr)
free(m_ptr);
m_ptr = nullptr;
}
}
};
void hd(Str&& str) {
printf("str.m_ptr = %pn", 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 = %pn", b.m_ptr); // it's been moved.
}
一般规则:
- 按值传递琐碎对象,
- 如果目标需要一个可变副本,则按值传递,
- 按值传递如果总是需要复制,
- 通过const引用传递非平凡对象,查看者只需要查看内容/状态,但不需要修改,
- 当目标需要临时/构造值的可变副本时移动(例如
std::move(std::string("a") + std::string("b")))
)。 - 当您需要对象状态的局域性,但希望保留现有值/数据并释放当前持有人时移动。
请记住,如果不传递r值,则按值传递将导致完整的副本。所以一般来说,按值传递可能会导致性能下降。
你的例子有缺陷。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});
- 何时应通过引用传递矢量参数而不是按值传递矢量参数?
- C++类 - 初始化列表 - 递归 - 按值传递
- 将函数参数完美转发到函数指针:按值传递呢?
- 棘手的按值传递和按引用递归问题传递
- 不同于按值传递和常量引用传递的程序集
- 按值传递变量与按引用传递变量具有相同的结果
- 为什么按值传递QStringView比引用常量更快?
- 获取 std::函数以推断按引用传递/按值传递
- 在函数中按值传递 unordered_map/unordered_set 是否有效? C++
- C++/11 auto 关键字是在更有效时推导参数进行按引用传递,还是始终按值传递?
- 在C++ Lambda 表达式中,为什么人们更喜欢按值捕获而不是作为参数传递?
- 对于可移动类型,按值传递比重载函数更好吗?
- 支持按值传递语句的示例不是很好的做法,即使对于小型用户定义类型也是如此
- 为什么C++编译器不在按值传递方案中优化更多的字符串构造?
- 为什么按值传递 int 比按引用传递更快
- 使用 Lambda 时,通过捕获子句按值传递还是通过作为参数传递来传递性能更高
- 在c++ 11中,When是比按值传递更好的const引用
- 哪个更好:按值或按常量引用返回std::string
- 是按值传递然后移动构造更好,还是按引用传递然后复制构造更好
- 按值传递比按引用传递更快