TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.37K subscribers
Post #123 1.51K
​Идеальная передача — perfect forwarding

В продолжение к предыдущему посту.

Мы теперь знаем, что универсальные ссылки могут работать с разными категориями выражений lvalue и xvalue. При написании кода шаблонной функции мы можем не знать, какие аргументы могут быть переданы в неё. Соответственно, мы не знаем, можем ли мы распоряжаться её внутренними ресурсами. Всё это сильно влияет на производительность нашего решения. Что же делать в такой ситуации?

Конечно, как вы уже знаете, мы можем детектировать тип rvalue reference. И да, мы можем написать два разных участка кода для двух разных категорий выражений. Можно, но нужно ли? Это противоречит дублированию кода.

Функция std::forward используется для так называемой идеальной передачи аргументов при вызове других методов, конструкторов и функций:
template<typename T>
void foo(T &&message)
{
T tmp(std::forward<T>(message));
...
}


В данном примере во временный объект tmp будет передано либо lvalue, либо xvalue. Следовательно, мы либо скопируем строку, либо переместим. Это зависит от того, как вызвали foo:
std::string str = "blah blah blah";

// Передает lvalue => std::string tmp(str);
foo(str);

// Передает xvalue => std::string tmp(std::move(str));
foo(std::move(str));


То есть std::forward выполняет проброс информации о категории выражения внутрь. Отсюда и название: forward, т.е. дальше.

Отлично, где же нам такая радость может пригодиться? Конечно же, при использовании универсальных ссылок. В основном, при написании оберток над чем-то.

Пример I. Инициализация объекта по универсальной ссылке:
template<class T>
class wrapper
{
std::vector<T> m_data;
public:
template<class Y>
wrapper(Y &&data)
: m_data(std::forward<Y>(data))
{
// make a copy from `data` or move resources from `data`
}
};


Пример II. При работе с контейнерами STL я предпочитаю использовать семейство функций emplace, т.к. они предоставляют возможность сконструировать объект сразу там, где он будет потом храниться. В основе таких методов лежит std::forward, который пробрасывает аргументы вплоть до конструкторов. Смотрите сами тут.

Передачу аргументов таким способом называют идеальной передачей (т.н. perfect forwarding), потому что она позволяет не создавать копии временных объектов.

Не забываем об исключениях в перемещающем конструкторе, а так же про оптимизации RVO/NRVO.

#cppcore #memory #algorithm
  • 👍 17
  • 🔥 10
  • ❤ 3
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →