Возможный дубликат:
передача шаблона по значению или константной ссылке или…?
Что является хорошей практикой для функции, принимающей функцию в качестве параметра:
template<class Function> void test1(Function f);
template<class Function> void test2(Function& f);
template<class Function> void test3(const Function& f);
где переданная функция может быть функтором, функцией std :: function, указателем функции или лямбда-функцией.
Используйте универсальные ссылки, и вам не нужно будет думать об этом:
template<class Function> void test(Function&& f);
Когда аргумент передается в шаблон функции, определяющий тип аргумента, возникает интересный вопрос о том, как должен передаваться аргумент. Стоит отметить, что точная природа использования аргумента не имеет значения: вопрос о том, используется ли аргумент в качестве объекта функции, итератора, значения и т. Д., Не имеет значения для ответа. Есть четыре варианта:
template <typename T> void f(T&& a)
template <typename T> void f(T& a)
template <typename T> void f(T const& a)
template <typename T> void f(T a)
Немаловажно, что на самом деле делает шаблон функции: если все f()
делает со своим аргументом a
это переслать его в другую функцию, которую вы хотите передать по универсальной ссылке. Вызываемая функция рассортирует детали и отклонит неподходящие параметры. Конечно, функции пересылки, хотя и полезны в целом, несколько скучны.
Если f()
фактически что-то делает с объектом, обычно мы можем сразу отказаться от двух опций:
T const&
нам тоже не очень хорошо: мы получили ссылку на объект, время жизни которого мы не можем контролировать и от которого мы не можем ни переместиться, ни получить его копирование / перемещение.Передача объектаconst
ссылка может быть полезна, если сам объект изменяется. Это, безусловно, важная часть контракта функции и навязанный выбор дизайна, который не может быть отменен клиентом функции при его выполнении.
Предпочтительный подход — принимать аргументы по значению. Как правило, это значительно упрощает определение поведения функции и фактически оставляет выбор, должен ли тип следовать за значением или ссылочной семантикой, открытой для пользователя! То, что что-то передается по значению, не означает, что объекты, представляющие интерес, также передаются по значению. Специально для случая объекта функции стандартная библиотека C ++ даже предоставляет универсальный адаптер, предоставляющий семантику ссылки на тип значения: std::ref()
,
Конечно, дизайн интерфейса тонкий, и будут случаи, когда каждый из вариантов оправдан. Однако, как правило, я думаю, что это очень просто:
… и, конечно, эти правила применяются только к аргументам шаблонов функций, тип которых выводится.