Подтвердить что ты не робот

Когда значение константы const лучше, чем значение pass-by-value в С++ 11?

У меня есть код pre-С++ 11, в котором я использую ссылки const для передачи больших параметров, таких как vector. Пример следующий:

int hd(const vector<int>& a) {
   return a[0];
}

Я слышал, что с новыми функциями С++ 11 вы можете передать vector по значению следующим образом без обращений к производительности.

int hd(vector<int> a) {
   return a[0];
}

Например, этот ответ говорит

С++ 11 перемещение семантики делает передачу и возврат по значению гораздо более привлекательным даже для сложных объектов.

Правда ли, что эти два параметра одинаковы по производительности?

Если да, то когда используется ссылка const, как в варианте 1, лучше, чем опция 2? (то есть зачем нам все еще нужно использовать ссылки const в С++ 11).

Одна из причин, по которой я спрашиваю, заключается в том, что ссылки на const ссылаются на вычет параметров шаблона, и было бы намного проще использовать только пропущенные значения, если это то же самое со ссылкой на const reference-wise.

4b9b3361

Ответ 1

Общее правило для передачи по значению - это когда вы в конечном итоге создадите копию. То есть, вместо этого:

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
}

Это частично было верно в С++ 03 и стало более полезным с семантикой перемещения, поскольку копия может быть заменена перемещением в случае pass-by-val, когда функция вызывается с rvalue.

В противном случае, когда все, что вы хотите сделать, - это прочитать данные, передача с помощью const ссылки по-прежнему является предпочтительным, эффективным способом.

Ответ 2

Есть большая разница. Вы получите копию внутреннего массива 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)

С++ 11, а введение ссылок rvalue изменило правила возврата объектов, таких как векторы, - теперь вы можете это сделать (не беспокоясь о гарантированной копии). Однако основные правила о том, чтобы принимать их в качестве аргумента, - вы все равно должны брать их по ссылке const, если вам действительно не нужна настоящая копия - возьмите по значению тогда.

Ответ 3

Помните, что если вы не проходите в r-значении, то передача по значению приведет к полномасштабной копии. Так что, вообще говоря, переход по значению может привести к поражению производительности.

Ответ 4

С++ 11 перемещение семантики делает передачу и возврат по значению гораздо более привлекательным даже для сложных объектов.

Образец, который вы даете, однако, является образцом прохода по значению

int hd(vector<int> a) {

Итак, С++ 11 не влияет на это.

Даже если вы правильно объявили 'hd', чтобы взять rvalue

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 been moved.
}

Как правило:

  • Перейдите по значению для тривиальных объектов,
  • Передайте по значению, если получателю нужна изменчивая копия,
  • Передайте по значению, если вам всегда нужно сделать копию,
  • Передавать по ссылке const для нетривиальных объектов, где зрителю требуется только просмотр содержимого/состояния, но он не нуждается в его модификации,
  • Переместить, когда получателю нужна изменчивая копия временного/сконструированного значения (например, std::move(std::string("a") + std::string("b"))).
  • Перемещение, когда вам требуется локальность состояния объекта, но вы хотите сохранить существующие значения/данные и освободить текущий держатель.

Ответ 5

Ваш пример испорчен. С++ 11 не дает вам двигаться с кодом, который у вас есть, и будет сделана копия.

Тем не менее, вы можете получить ход, объявив функцию, чтобы взять ссылку rvalue, а затем передать ее:

int hd(vector<int>&& a) {
   return a[0];
}

// ...
std::vector<int> a = ...
int x = hd(std::move(a));

Предположим, что вы больше не будете использовать переменную a в своей функции, кроме как уничтожить ее или присвоить ей новое значение. Здесь std::move выводит значение на ссылку rvalue, позволяя двигаться.

Ссылки на константу позволяют временно создавать временные файлы. Вы можете передать то, что подходит для неявного конструктора, и будет создано временное. Классическим примером является массив 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});