Страницы

Поиск по вопросам

четверг, 12 декабря 2019 г.

Почему примитивы нужно передавать по значению?

#cpp


Почему примитивные типы данных нужно передавать по значению, а не по константной ссылке?
    


Ответы

Ответ 1



Для начала подумайте, зачем передавать большие объекты по ссылке, а не по значению? Ответ на это такой: чтобы избежать копирования. При передаче по ссылке вместо самого объекта передаётся ссылка на него (то есть, под капотом, указатель), так что расходов на копирование объекта нет. У нас при этом, правда, получается расход на косвенный вызов (по указателю), но им можно обычно пренебречь. Теперь, что получается, если мы передаём по ссылке примитивные типы? Их размер сравним с размером самой ссылки, а то и меньше. Таким образом, выгоды от передачи по ссылке нет, копирование что самого примитивного типа, что ссылки — примерно одинаковая операция. А вот затраты на разыменование ссылки остаются. Поэтому имеет смысл по возможности избегать передачи примитивных типов по ссылке. С другой стороны, компиляторы становятся всё умнее, и заинлайнив функцию, вполне могут перейти от ссылки к самому объекту, тем самым «уравняв» оба подхода. Смысл-то реально один: передача объекта в функцию.

Ответ 2



Примитивные типы данных очень дешево копируются, по этому их можно передавать по значению. Однако это не означает что их нельзя передавать по ссылке. Очевидно, что если функция должна поменять переменную, то ее надо передавать по не константной ссылке (указателю). В обобщенном коде (шаблонах), когда мы не знаем тип объекта, аргументы тоже можно передавать по ссылке. Но также возможна ситуация, когда что-то еще может поменять переменную. Тогда ее тоже надо передавать по ссылке. Например: R g(int* var) { *var = 1; return ...; } void f(R, ??? var); int x = 0; f(g(&x), x); Если сигнатура f это f(T, int), то будет неопределенное поведение (UB), т.к. порядок передачи аргументов не определен, и x может измениться как до того как ее поменяет g(&x), так и после. Если сигнатура f это f(T, const int&) - то всё хорошо, в f будет передана ссылка на уже измененную переменную. Разумеется такой код лучше не писать. Но в природе такое встречается, коллега недавно нашел аналогичный код в WebKit.

Ответ 3



При передаче по значению вы гарантировано (с т.з. легальных средств языка) работаете с тем самым значением, которое передавалось в функцию, а вот при передаче по ссылке таких гарантий в общем случае нет. Например, если в функцию f1(int& arg) передали по ссылке глобальную переменную int x, то вызванная из f1 функция f2 изменив x изменит и аргумент arg в функции f1. Меняя аргумент, переданный по ссылке, вы в тот же момент меняете переменную в вызывающей функции, а при передаче по значению меняется его копия. Опять же, следует иметь в виду, что если программист использует передачу по ссылке, то при чтении такой программы другим программистом без рассмотрения текста вызываемой функции (или ее прототипа) неясно, может ли передаваемая переменная измениться при вызове функции, что замедляет понимание такой программы и поиск ошибок. В случае, когда собираетесь менять в вызываемой функции передаваемую ей переменную, семь раз подумайте, что лучше -- явно передавать в функцию указатель на переменную или сэкономить на наборе нескольких символов и замаскировать передаваемый указатель (т.е. передавать по ссылке).

Ответ 4



Да требования такого строгого нет, как и нет особой разницы. Просто все эти типы очень небольшие по размеру, а передача по ссылке по сути есть передача адреса и косвенное обращение - так ради чего возиться? Экономии никакой, одни накладные расходы. Для иллюстрации - простое возведение в квадрат без оптимизации, VC++ 2015. Реализация по значению - 2 ассемблерные команды, по ссылке - 4. Вызов функции - тоже 2 и 4...

Ответ 5



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

Комментариев нет:

Отправить комментарий