Есть ли преимущество в использовании std :: forward вместо std :: move для инициализации объекта? - PullRequest
9 голосов
/ 08 июля 2019

У меня есть шаблон функции, который принимает аргумент некоторого вызываемого типа и использует std::bind для создания нового вызываемого объекта с предопределенными значениями аргумента для исходного вызываемого объекта.Я написал его с опорным параметром перенаправления и std::forward, например:

template <typename F>
auto make_example_caller(F &&f) {
  return std::bind(std::forward<F>(f), 123, 456, 789);
}

Документация cppreference для std::bind говорит, что связанный объект "содержит объект-члентипа std::decay<F>::type, построенный из std::forward<F>(f) ".Поскольку std::bind перенаправляет функцию на свой внутренний элемент данных, перенаправление этой же функции на вызов std::bind в моем собственном коде кажется разумным и уместным.

Однако неясно, какую пользу это дает.Если F является ссылочным типом, std::decay удаляет ссылку, поэтому объект связывания будет хранить свой собственный экземпляр вызываемого типа.Этот экземпляр будет создан как ход, если F является ссылкой на rvalue, или копия, если F является lvalue, и я могу получить тот же результат, если напишу свою функцию следующим образом:

template <typename F>
auto make_example_caller(F f) {  // Note, no &&
  return std::bind(std::move(f), 123, 456, 789);  // move, not forward
}

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

Последний способ кажется более простым, но мне интересно, если я что-то упустил - тем более, что те же рассуждения применимы к самому std::bind, но для этого нужно F&& и пересылать его вместо F по значению и перемещая его.Есть ли недостаток в этом?Что-то, чего я не вижу?

1 Ответ

6 голосов
/ 08 июля 2019

Используя ссылку для пересылки и std::forward, вы можете исключить создание дополнительного объекта.

Если вы не используете ссылку для пересылки, у вас есть три объекта:

  1. Исходный объект звонящего
  2. Параметр функции f, построенный с использованием соответствующего конструктора копирования или перемещения
  3. Связать внутренний объект объекта, построенный конструктором перемещения

Если вы используете ссылку переадресации с std::forward, вы исключаете второй. Будет создано только два объекта:

  1. Исходный объект звонящего
  2. Связать внутренний объект объекта, созданный с использованием конструктора копирования или перемещения, в зависимости от ситуации

Хотя создание объекта с помощью перемещения может быть дешевле, чем создание объекта с копированием (в зависимости от типа), оно по-прежнему вносит некоторые накладные расходы, которых может избежать идеальная пересылка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...