Передача аргументов встроенной функции

Есть ли потребность в производительности, чтобы встроенные функции передавали свои аргументы по константной ссылке, например

foo(const T & a, const T &b)

по сравнению с по стоимости

foo(T a, T b)

если я не изменю значения a и b в функции? Рекомендует ли изменение С++ 11 что-то конкретное здесь?


person Nordlöw    schedule 02.10.2011    source источник
comment
Значение — это значение, а ссылка — это ссылка. Передача ссылки на константу, когда вместо этого имеется в виду передача значения, является ошибкой, которая может сильно укусить. См. stackoverflow.com/questions/4705593/int-vs -const-int/   -  person 6502    schedule 02.10.2011
comment
@ 6502: Это отличный пример случая, когда использование ссылки усложняет логику (v.push_back(v[0]) допустимо, потому что стандартная библиотека должна включать эту дополнительную логику).   -  person Ben Voigt    schedule 02.10.2011
comment
@BenVoigt: Приятно видеть, что этот запрос был добавлен в стандарт. Где указано?   -  person 6502    schedule 02.10.2011
comment
@ 6502: Если ссылка действительна при вызове push_back, стандартная библиотека обязана правильно с ней поступить.   -  person Ben Voigt    schedule 02.10.2011
comment
@BenVoigt: Извините, но я не согласен. При вызове функции, передающей ссылку, если ссылочный объект не живет достаточно долго для продолжительности функции, это проблема вызывающей стороны (вызываемый не имеет над ней контроля). AFAIK стандарт не говорит, что операция копирования должна быть выполнена до освобождения: если реализация хочет сделать это, все в порядке, но действительный компилятор C ++ может заставить демонов слетать с моего носа, оставаясь при этом совместимым. Кстати, теперь с C++0x все еще сложнее... может ли перераспределение использовать конструктор перемещения? Если это так, просто отложить уничтожение старого хранилища недостаточно...   -  person 6502    schedule 03.10.2011
comment
@ 6502: Это то, что разработчик библиотеки может контролировать, а вызывающий - нет. Вызывающий объект отвечает за то, чтобы не удалять переданный объект во время выполнения функции (например, из обратного вызова или другого потока), а вызываемый объект отвечает за то, чтобы не удалять переданный объект до тех пор, пока в нем больше нет необходимости.   -  person Ben Voigt    schedule 03.10.2011
comment
Во всяком случае, уже доказано, что v.push_back(v[0]) безопасен: an-element-of-a-conta/6212163#6212163" title="как правильно избежать псевдонимов, например, при добавлении элемента conta"> stackoverflow.com/questions/6210688/   -  person Ben Voigt    schedule 03.10.2011
comment
@BenVoigt: также после обсуждения этого в чате Альф П. Штайнбах сделал еще один важный вывод (chat.stackoverflow.com/transcript /message/1594676#1594676). Если вы передаете функции ссылку на объект, который не живет достаточно долго, то вы уже находитесь в стране UB, и реализация C++ не обязана вести себя каким-либо предписанным образом. v[0] не будет жить достаточно долго в случае перераспределения, и поэтому реализация может делать все, что захочет.   -  person 6502    schedule 03.10.2011
comment
@ 6502: Но вы передаете ссылку, которая ДЕЙСТВИТЕЛЬНА. Так что вы не в UB-земле. Если вызывающий объект сделал ссылку недействительной, это будет на вызывающем объекте. Но если стандартная библиотека делает ссылку недействительной, ей не нужно впоследствии использовать эту ссылку.   -  person Ben Voigt    schedule 03.10.2011
comment
@BenVoigt: я нахожусь в стране UB, потому что я передаю ссылку на объект, который не будет жить достаточно долго, потому что он действителен только до тех пор, пока не произойдет перераспределение. Так что это моя вина (вызывающий) передать его функции, которой может потребоваться перераспределение, возможно, перед его использованием. Как правило, вызываемая функция не имеет возможности контролировать, как долго будет жить объект, полученный по ссылке, поэтому ответственность за то, чтобы он прожил достаточно долго, лежит на вызывающем объекте. Представьте, что я передаю push_back ссылку на объект, который уничтожается при вызове ::operator new... все равно будет ошибкой библиотеки?   -  person 6502    schedule 03.10.2011
comment
@ 6502: Но есть явная разница между объектом, уничтоженным самим push_back, и объектом, уничтоженным кодом, который контролирует только вызывающая сторона.   -  person Ben Voigt    schedule 03.10.2011
comment
(Постскриптум к комментариям выше - я принял сторону 6502 в другом обсуждении, и никто не согласился)   -  person Ben Voigt    schedule 24.01.2014


Ответы (3)


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

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

person Ben Voigt    schedule 02.10.2011
comment
В дополнение к проблемам с псевдонимами существуют проблемы с продолжительностью жизни. - person 6502; 02.10.2011
comment
@ 6502: Определенно по возвращаемым значениям. Для параметров могут возникать проблемы с продолжительностью жизни, но они очень редки. И я бы в любом случае считал их подмножеством проблем с псевдонимами. - person Ben Voigt; 02.10.2011
comment
@BenVoigt Passing primitive types by const reference will have no cost when the function is inlined. Вы имеете в виду, что не будет никакой выгоды с точки зрения стоимости? Можете ли вы подтвердить, есть ли что-то, что можно получить, передавая const int& или const int, когда функция встроена? - person Antonio; 24.01.2014
comment
@ Антонио: Нет, я имел в виду то, что сказал. Как правило, передача по ссылке const сопряжена со значительными затратами: вызываемый объект должен получить доступ к указателю в дополнение к фактическим данным, что означает удвоенный доступ к памяти. Вызываемый объект также должен беспокоиться о алиасинге, который препятствует некоторым оптимизациям. Когда функция встроена, компилятор может определить, действительно ли возможно использование псевдонимов, и часто может выполнить эти оптимизации и получить доступ к данным напрямую, а не через дополнительный указатель. - person Ben Voigt; 24.01.2014

Теоретически те, на которые нет ссылки, могут быть скопированы в память, поскольку существует вероятность того, что ваша встроенная функция может их изменить (даже если на самом деле это не так).

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

Используя ссылку const, вы можете дать ей довольно четкое указание.

РЕДАКТИРОВАТЬ: я просто хочу взглянуть на машинный код простой тестовой программы, скомпилированной с помощью GCC 4.6 в ddd. Сгенерированный код казался идентичным, поэтому он кажется оптимизированным. Это по-прежнему хорошая практика для других компиляторов, и, по крайней мере, дает четкое указание на намерение кода. Также возможно, что существуют более сложные ситуации, которые компилятор не может оптимизировать.

Кроме того, демонстрация онлайн-диссемблера llvm показывает, что там тоже генерируется идентичный битовый код. Если вы отключите оптимизацию, она будет немного длиннее без ссылки на константу.
* 1964 байта — без ссылки на константу (и никаких других констант в функциях/параметрах)
* 1960 байт — просто без ссылки на константу, но другие константы .
* 1856 байт — с константами и ссылкой на константу.

person David C. Bishop    schedule 02.10.2011
comment
Если встроенная функция передает a и b другой функции, оптимизация копии может оказаться невозможной. Например, если встроенная функция вызывает не встроенную функцию bar(a), компилятор должен скопировать, потому что bar может выполнить if (&a == &special) ..., а первоначальный вызывающий объект мог передать special. - person Raymond Chen; 02.10.2011
comment
Попробуйте еще раз с типом, отличным от POD. - person Ben Voigt; 02.10.2011
comment
Для оптимизатора не имеет значения, объявлена ​​ли функция-член const или нет. Const-корректность невидима для оптимизатора и предназначена только для помощи программистам (выдавая ошибки времени компиляции, если вы нарушаете объявления const), а не для оптимизатора. - person 6502; 02.10.2011
comment
@ 6502: const-correctity действительно влияет на оптимизацию в некоторых случаях, хотя я согласен, что константность функции-члена не является одной из них. - person Ben Voigt; 02.10.2011
comment
@Ben Voigt: На оптимизацию могут повлиять постоянные значения. Постоянство ссылки (как в const X &) или указателя (как в const X *) никогда не помогает оптимизатору, потому что это объявление, которое ничего не говорит о константности объекта, на который ссылаются или на который указывают (эти объявления только определяют какие операции допустимы для указателя/ссылки). - person 6502; 02.10.2011

Передача по ссылке быстрее, чем по значению, в зависимости от типа данных.
Однако для встроенных функций тело функции (и, следовательно, все ссылки/переданные значения) добавляются к строке кода, в которой они используются в любом случае, поэтому технически нет переменные передаются, только больше строк кода в той же области.

ссылка http://www.cprogramming.com/tutorial/lesson13.html

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

  • Пример мог ввести в заблуждение, удален -
person Serdalis    schedule 02.10.2011
comment
Это полностью предположение. - person pmr; 02.10.2011
comment
Передача по ссылке выполняется быстрее, чем по значению в зависимости от типа. Кроме того, код, использующий ссылку, имеет дополнительные требования (для правильной обработки псевдонимов) и может потребовать дополнительной косвенности, которая не требуется для собственных типов, передаваемых по значению. - person 6502; 02.10.2011
comment
Извините, по какой-то странной причине я получил проход по указателю, смешанному со значением, вы правы, это быстрее только для некоторых типов данных. Я также указываю вам stackoverflow.com/questions/722257/, что поддерживает мою точку зрения - person Serdalis; 02.10.2011