Лучше ли в C ++ передавать по значению или передавать по константе?
Мне интересно, что лучше практиковать. Я понимаю, что передача по постоянной ссылке должна обеспечить лучшую производительность в программе, потому что вы не делаете копию переменной.
c++
variables
pass-by-reference
constants
pass-by-value
Мэтт Паско
источник
источник
Ответы:
Раньше обычно рекомендуются наилучшая практика 1 для использования прохода по константному иому для всех типов , для встроенных типов (кроме
char
,int
,double
и т.д.), для итераторов и функциональных объекты (лямбды, классов , вытекающие изstd::*_function
).Это было особенно верно до существования семантики перемещения . Причина проста: если вы передали по значению, нужно было сделать копию объекта, и, за исключением очень маленьких объектов, это всегда дороже, чем передача ссылки.
С C ++ 11 мы получили семантику перемещения . Вкратце, семантика перемещения позволяет в некоторых случаях передавать объект «по значению», не копируя его. В частности, это тот случай , когда объект , который вы передаете является Rvalue .
Само по себе перемещение объекта по крайней мере так же дорого, как передача по ссылке. Однако во многих случаях функция все равно будет внутренне копировать объект - т.е. она получит владение аргументом. 2
В этих ситуациях мы имеем следующий (упрощенный) компромисс:
«Передача по значению» по-прежнему вызывает копирование объекта, если только объект не является значением. В случае значения r объект можно вместо этого переместить, так что второй случай внезапно перестает быть «копировать, затем перемещать», а «перемещать, а затем (потенциально) перемещать снова».
Для больших объектов, которые реализуют правильные конструкторы перемещения (такие как векторы, строки…), второй случай тогда намного более эффективен, чем первый. Поэтому рекомендуется использовать проход по значению, если функция получает владение аргументом и если тип объекта поддерживает эффективное перемещение .
Историческая справка:
Фактически, любой современный компилятор должен быть в состоянии выяснить, стоит ли передавать по значению дорого, и неявно преобразовывать вызов, чтобы использовать const ref, если это возможно.
Теоретически. На практике компиляторы не всегда могут изменить это, не нарушая двоичный интерфейс функции. В некоторых особых случаях (когда функция встроена) копия будет фактически удалена, если компилятор может выяснить, что исходный объект не будет изменен посредством действий в функции.
Но в целом компилятор не может определить это, и появление семантики перемещения в C ++ сделало эту оптимизацию гораздо менее актуальной.
1 Например, Скотт Мейерс, Эффективный C ++ .
2 Это особенно часто верно для конструкторов объектов, которые могут принимать аргументы и сохранять их внутри, чтобы быть частью состояния построенного объекта.
источник
Изменить: Новая статья Дэйва Абрахамса на cpp-next:
Хотите скорость? Передать по значению.
Передача по значению для структур, где копирование дешево, имеет дополнительное преимущество, заключающееся в том, что компилятор может предположить, что объекты не являются псевдонимами (не являются одинаковыми объектами). Используя передачу по ссылке, компилятор не может предполагать это всегда. Простой пример:
компилятор может оптимизировать его в
так как он знает, что f и g не находятся в одном месте. если бы g был ссылкой (foo &), компилятор не мог бы это предположить. поскольку в этом случае gi может быть псевдонимом f-> i и должно иметь значение 7. Таким образом, компилятор должен будет повторно извлечь новое значение gi из памяти.
Для более практичных правил, вот хороший набор правил, найденных в статье Move Constructors (настоятельно рекомендуется к прочтению).
«Примитив» выше означает в основном небольшие типы данных, которые имеют длину несколько байтов и не являются полиморфными (итераторы, объекты функций и т. Д.) Или дорогостоящими для копирования. В этой статье есть еще одно правило. Идея состоит в том, что иногда кто-то хочет сделать копию (в случае, если аргумент не может быть изменен), а иногда он не хочет (в случае, если кто-то хочет использовать сам аргумент в функции, если в любом случае аргумент был временным , например). В документе подробно объясняется, как это можно сделать. В C ++ 1x эта техника может быть использована изначально с поддержкой языка. До тех пор я бы пошел с вышеуказанными правилами.
Примеры: Чтобы создать строку в верхнем регистре и вернуть версию в верхнем регистре, нужно всегда передавать по значению: в любом случае нужно взять ее копию (нельзя напрямую изменить ссылку на const) - так что лучше сделать ее максимально прозрачной, чтобы вызывающего и сделайте эту копию заранее, чтобы вызывающий мог оптимизировать как можно больше - как подробно описано в этой статье:
Однако, если вам все равно не нужно изменять параметр, возьмите его по ссылке на const:
Однако если целью параметра является запись чего-либо в аргумент, то передайте его по неконстантной ссылке
источник
__restrict__
(которые также могут работать со ссылками), чем делать чрезмерные копии. Жаль, что стандартный C ++ не принялrestrict
ключевое слово C99 .Зависит от типа. Вы добавляете небольшие накладные расходы на необходимость ссылки и разыменования. Для типов с размером, равным или меньшим, чем указатели, которые используют ctor копирования по умолчанию, вероятно, будет быстрее передать по значению.
источник
Как уже было отмечено, это зависит от типа. Для встроенных типов данных лучше всего передавать по значению. Даже некоторые очень маленькие структуры, такие как пара целых, могут работать лучше, передавая значение.
Вот пример, предположим, что у вас есть целочисленное значение, и вы хотите передать его другой подпрограмме. Если это значение было оптимизировано для хранения в регистре, то, если вы хотите передать его как ссылку, оно сначала должно быть сохранено в памяти, а затем указатель на эту память помещен в стек для выполнения вызова. Если он был передан по значению, все, что требуется, - это регистр, помещенный в стек. (Детали немного сложнее, чем для разных систем вызова и процессоров).
Если вы занимаетесь программированием шаблонов, вы обычно вынуждены всегда проходить через const ref, так как не знаете, какие типы передаются. Передача штрафов за передачу чего-то плохого по значению намного хуже, чем штрафы за передачу встроенного типа. по конст. исх.
источник
Это то, чем я обычно работаю при разработке интерфейса не шаблонной функции:
Передайте по значению, если функция не хочет изменять параметр, а значение дешево копировать (int, double, float, char, bool и т. Д. ... Обратите внимание, что std :: string, std :: vector и остальные из контейнеров в стандартной библиотеке нет)
Передача по указателю const, если значение дорого копировать, и функция не хочет изменять указанное значение, а NULL - это значение, которое обрабатывает функция.
Передайте неконстантный указатель, если значение дорого копировать, и функция хочет изменить указанное значение, а NULL - это значение, которое обрабатывает функция.
Передача по константной ссылке, когда значение копируется слишком дорого, и функция не хочет изменять указанное значение, а NULL не будет допустимым значением, если вместо него будет использован указатель.
Передача по неконстантной ссылке, когда значение копируется дорого, и функция хочет изменить упомянутое значение, а NULL не будет допустимым значением, если вместо него будет использован указатель.
источник
std::optional
к картинке и вам больше не нужны указатели.Похоже, вы получили свой ответ. Передача по значению стоит дорого, но дает вам копию для работы, если вам это нужно.
источник
Как правило, лучше передавать по константной ссылке. Но если вам нужно изменить аргумент вашей функции локально, вам лучше использовать передачу по значению. Для некоторых базовых типов производительность в целом одинакова как для передачи по значению, так и по ссылке. Фактически ссылка, представленная указателем, поэтому вы можете ожидать, например, что для указателя обе передачи одинаковы с точки зрения производительности, или даже передача по значению может быть быстрее из-за ненужной разыменования.
источник
Как правило, значение для не-классовых типов и константная ссылка для классов. Если класс действительно маленький, то, вероятно, лучше передать по значению, но разница минимальна. Чего вы действительно хотите избежать, так это передавая некоторый гигантский класс по значению и дублируя его - это будет иметь огромное значение, если вы передадите, скажем, std :: vector с довольно большим количеством элементов в нем.
источник
std::vector
самом деле размещает свои элементы в куче, а сам векторный объект никогда не растет. Ой, подожди. Однако, если операция вызывает создание копии вектора, она на самом деле будет дублировать все элементы. Это было бы плохо.sizeof(std::vector<int>)
является константой, но передача его по значению будет копировать содержимое при отсутствии какой-либо хитрости компилятора.Передайте по значению для небольших типов.
Передача по константным ссылкам для больших типов (определение больших может варьироваться в зависимости от машины), НО в C ++ 11 передается по значению, если вы собираетесь использовать данные, поскольку вы можете использовать семантику перемещения. Например:
Теперь вызывающий код будет делать:
И только один объект будет создан и перемещен непосредственно в член
name_
классаPerson
. Если вы передадите константную ссылку, для ее размещения потребуется сделать копиюname_
.источник
Простая разница: - В функции у нас есть входной и выходной параметр, поэтому, если передаваемый входной и выходной параметры одинаковы, используйте вызов по ссылке, иначе, если входной и выходной параметры отличаются, лучше использовать вызов по значению.
пример
void amount(int account , int deposit , int total )
входной параметр: счет, депозитный выходной параметр: всего
вход и выход отличается использовать вызов Vaule
void amount(int total , int deposit )
итоговая сумма
источник