Зачем вам передавать объект по значению в С++

Возможный дубликат:
Лучше ли в С++ передавать значение или передавать постоянную ссылку?

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

void foo(Obj o); ... // Bad

void foo(const Obj &o); ... // Better

Единственный случай, когда я могу подумать о том, где целесообразно передавать значение вместо ссылки на const, является то, где объект меньше, чем ссылка, и поэтому передача по значению является более эффективной.

Но, конечно, это то, что компиляторы построены для определения?

Почему С++ действительно нуждается в передаче по значению И передается по ссылке const, и - компиляторы разрешают автоматически конвертировать вызов (и из) ссылки на константу, если это необходимо?

(Кажется, что существует 100-значный вопрос о вызове на С++, спрашивающий о различиях между (скажем) значением и ссылкой - но я не мог найти тот, который спросил "почему?".)

Ответы

Ответ 1

Вопрос о том, когда передача по значению может быть лучше, чем константная ссылка, имеет разные ответы с разными версиями стандарта.

В добром старом С++ 03, и несколько лет назад, рекомендация состояла бы в том, чтобы передать все, что не вписывается в регистр по ссылке const. В этом случае ответ будет следующим:

  • Потому что Obj вписывается в регистр и передается по значению, а передача по значению будет более эффективной

Еще в С++ 03, в последние годы (абсурдно, как кажется, некоторые статьи рекомендовали это почти 10 лет назад, но не было никакого реального согласия),

  • если функция должна сделать копию, тогда выполнение этого в интерфейсе позволяет компилятору выполнить копирование, если источник для копии является временным, поэтому он может быть более эффективным.

С одобрения нового стандарта С++ 11 и увеличения поддержки компилятора для rvalue-ссылок во многих случаях даже когда копия не может быть удалена, и снова

  • если функция должна сделать копию, даже если копия не может быть удалена, а для типов, которые ее поддерживают, содержимое будет перемещено (в общем жаргоне объект будет перемещен, но это будет только содержимое сдвинутый), что снова будет более эффективным, чем копирование внутри.

Что касается вопроса о том, почему два разных соглашения о вызовах имеют разные цели. Передача по значению позволяет функции изменять состояние аргумента без вмешательства в исходный объект. Кроме того, состояние исходного объекта не будет мешать функции (рассмотрите многопоточную среду и поток, изменяющий источник, пока функция все еще выполняется).

Ответ 2

Конечно, одна из причин, по которой С++ имеет значение pass-by-value, заключается в том, что она унаследовала его от C, и удаление, которое может сломать код для небольшого усиления.

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

Другой менее очевидный случай, однако, заключается в том, что у вас есть функция, которая по какой-то причине нуждается в копии аргумента:

void foo(const Obj& obj)
{
    if(very_rare_check()) return;

    Obj obj_copy(obj);
    obj_copy.do_work();
}

В этом случае обратите внимание, что вы вынуждаете копию. Но предположим, что вы вызываете эту функцию с результатом другой функции, которая возвращает значение:

Obj bar() { return Obj(parameters); }

И назовите его так: foo(bar());

Теперь, когда вы используете версию ссылки const, компилятор завершит создание двух объектов: временного и копии в foo. Если, однако, вы передали по значению, компилятор может оптимизировать все временные параметры в местоположении, используемом параметром байта значения foo.

Там есть отличная статья об этом и переместите семантику вообще в http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

Наконец, канонический способ реализации определенных операторов состоит в использовании pass-by-value, чтобы избежать копирования внутри оператора:

Obj operator+(Obj left, const Obj& right)
{
    return left += right;
}

Обратите внимание, как это позволяет компилятору сгенерировать копию в параметре, а не принуждать копию или временный объект внутри самого кода оператора.

Ответ 3

Если бы я хотел сделать что-то для объекта внутри функции, не затрагивая оригинал, я бы прошел по значению:

A minus(A b){
    b.val=-b.val;
    return b;
}

Ответ 4

Идентификатор обмена копиями использует значение passe по значению для достижения генерируемой компилятором копии.

MyClass& operator=(MyClass value) // pass by value to generate copy
{
    value.swap(*this);            // Now do the swap part.
    return *this;
}

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

MyClass a;
// Some code
a = MyClass(); // reset the value of a
               // compiler can easily elide this copy.

Ответ 5

Если объект изменен, передача по значению дает приемнику свою собственную копию для использования и там, где разумное изменение, не затрагивая копию вызывающего абонента, - всегда считая ее достаточно глубокой.

Это может упростить мышление в некоторых многопоточных ситуациях.

Ответ 6

Почему С++ действительно нуждается в передаче по значению И передается по ссылке const, и - компиляторы разрешают автоматически конвертировать вызов (и из) ссылки на константу, если это необходимо?

Позвольте мне сначала ответить на второй: иногда.

Составителям разрешено исключать копию в параметр, но только если вы передаете временное значение rvalue. Например:

void foo(Obj o);

foo((Obj()))); //Extra set of parenthesis are needed to prevent Most Vexing Parse

Копирование временного параметра аргумента может быть отменено (т.е. не скопировано) при удобстве компилятора).

Однако эта копия никогда не будет удалена:

Obj a;
foo(a);

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