Когда ссылка const лучше, чем pass-by-value в C++11?
у меня есть некоторый код pre-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 делает передачу и возврат по значению намного более привлекательными даже для сложных объекты.
правда ли, что эти два варианта одинаковы по производительности?
если да, то когда использование ссылки const, как в варианте 1, лучше, чем вариант 2? (т. е. Почему нам все еще нужно использовать ссылки const в C++11).
одна из причин, по которой я спрашиваю, заключается в том, что ссылки на const усложняют вычет параметров шаблона, и было бы намного проще использовать только pass-by-value, если это то же самое с const reference performance-wise.
5 ответов:
общее эмпирическое правило для передачи по значению - это когда вы в конечном итоге сделаете скопировать в любом случае. То есть, вместо того, чтобы делать это:
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, когда функция вызывается с rvalue.
в противном случае, когда все вы хотите сделать, это прочитать данные, проходя мимо
const
ссылка по-прежнему является предпочтительным, эффективным способом.
есть большая разница. Вы получите копию
vector
' s внутренний массив, если он не собирался умереть.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 изменили правила о возврате объектов, таких как векторы - теперь вы можно сделать (не беспокоясь о гарантированной копии). Нет основных правил, о С они в качестве аргумента изменились, хотя - вы все равно должны взять их по ссылке const, если вам действительно не нужна реальная копия-возьмите тогда по значению.
помните, что если вы не передаете значение r, то передача по значению приведет к полномасштабной копии. Так что, вообще говоря, передача значения может привести к удару по производительности.
семантика перемещения C++11 делает передачу и возврат по значению намного более привлекательными даже для сложных объектов.
образец, который вы даете, однако, является образцом pass by value
int hd(vector<int> a) {
так что C++11 не имеет никакого влияния на это.
даже если вы правильно объявили "hd", чтобы взять rvalue
int hd(vector<int>&& a) {
это может быть дешевле, чем pass-by-value, но выполнение удачный ход (в отличие от простого
std::move
который может не иметь никакого эффекта вообще) может быть дороже, чем простой pass-by-reference. Новый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. }
как правило:
- передать значение для тривиальных объектов,
- передать значение, если назначение нуждается в изменяемой копии,
- передать значение, если вы всегда нужно сделать копию,
- пройти по ссылке const для нетривиальных объектов, где зрителю нужно только увидеть содержимое / состояние, но не нужно, чтобы оно было модифицируемый,
- перемещение, когда адресату требуется изменяемая копия временного / построенного значения (например,
std::move(std::string("a") + std::string("b")))
.- перемещение, когда вам требуется локальность состояния объекта, но вы хотите сохранить существующие значения / данные и освободить текущий держатель.
ваш пример ошибочен. В C++11 не дает вам двигаться с кодом, который у вас есть, и будет создана копия.
однако вы можете получить движение, объявив функцию для получения ссылки rvalue, а затем передав ее:
int hd(vector<int>&& a) { return a[0]; } // ... std::vector<int> a = ... int x = hd(std::move(a));
это предполагает, что вы не будете использовать переменную
a
в вашей функции снова, кроме как уничтожить его или присвоить ему новое значение. Вот,std::move
приводит значение к ссылке rvalue, позволяя двигаться.ссылки Const позволяют создавать временные файлы без вывода сообщений. Вы можете передать что-то, что подходит для неявного конструктора, и будет создан временный. Классический пример-массив символов, преобразуемый в
const std::string&
но сstd::vector
, astd::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});