Идеальная переадресация


Идеальная переадресация

Что подразумевается под идеальной пересылкой?

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

Другими словами, идеальная пересылка означает, что мы не просто передаем объекты, мы также передаем их важные свойства, будь то lvalues или rvalues, const или volatile.

Не беспокойтесь слишком много об определении, мы рассмотрим несколько простых примеров.

Допустим, у нас есть простой класс, как показано ниже

class Object {
 public:
  Object() = default;

  void SetName(const std::string &name) { name_ = std::move(name); }
  std::string GetName() const { return name_; }

 private:
  std::string name_;
};
Вход в полноэкранный режим Выход из полноэкранного режима

У нас также есть несколько перегруженных функций UseObject.

void UseObject(Object &) {
  std::cout << "calling UseObject(Object &)" << std::endl;
}

void UseObject(const Object &) {
  std::cout << "calling UseObject(const Object &)" << std::endl;
}

void UseObject(Object &&) {
  std::cout << "calling UseObject(Object &&)" << std::endl;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь у нас есть главная

int main() {
  Object object;
  const Object const_object;
  UseObject(object);
  UseObject(const_object);
  UseObject(std::move(object));
}
Вход в полноэкранный режим Выход из полноэкранного режима

что приведет к следующему результату

calling UseObject(Object &)
calling UseObject(const Object &)
calling UseObject(Object &&)
Войти в полноэкранный режим Выход из полноэкранного режима

Сейчас, допустим, у нас есть простая шаблонная функция, которая пытается передать аргумент функции UseObject.

template <typename T>
void NotForwardToUseObject(T x) {
  UseObject(x);
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь, запуск кода

int main() {
  Object object;
  const Object const_object;
  NotForwardToUseObject(object);
  NotForwardToUseObject(const_object);
  NotForwardToUseObject(std::move(object));
}
Вход в полноэкранный режим Выйти из полноэкранного режима

приведет к тому, что

calling UseObject(Object &)
calling UseObject(Object &)
calling UseObject(Object &)
Войти в полноэкранный режим Выход из полноэкранного режима

где функции вызываются не так, как мы ожидали ранее.

Это происходит из-за того, что const и rvalueness игнорируются вычитанием шаблона для void NotForwardToUseObject(T x) .

Для работы со ссылочными параметрами мы должны использовать универсальные ссылки, потому что только универсальные ссылочные параметры кодируют информацию о lvalueness и rvalueness аргументов, которые им передаются.

Теперь, если мы используем универсальную ссылку для аргумента шаблона,

template <typename T>
void HalfForwardToUseObject(T &&x) {  // universal reference
  UseObject(x);
}
Вход в полноэкранный режим Выход из полноэкранного режима

Выполнение кода

int main() {
  Object object;
  const Object const_object;
  HalfForwardToUseObject(object);
  HalfForwardToUseObject(const_object);
  HalfForwardToUseObject(std::move(object));
}
Вход в полноэкранный режим Выйти из полноэкранного режима

приведет к тому, что

calling UseObject(Object &)
calling UseObject(const Object &)
calling UseObject(Object &)
Вход в полноэкранный режим Выход из полноэкранного режима

Почти! Пересылка const вроде бы работает, но rvalueness аргумента все еще не пересылается правильно.

Чтобы получить действительно идеальную пересылку, мы должны привести x к его исходному типу и l- или r-значению.

template <typename T>
void ForwardToUseObject(T &&x) {
  UseObject(static_cast<T &&>(x));
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь, выполнение кода

int main() {
  Object object;
  const Object const_object;
  ForwardToUseObject(object);
  ForwardToUseObject(const_object);
  ForwardToUseObject(std::move(object));
}
Вход в полноэкранный режим Выйти из полноэкранного режима

приведет к тому, что

calling UseObject(Object &)
calling UseObject(const Object &)
calling UseObject(Object &&)
Войти в полноэкранный режим Выйти из полноэкранного режима

Отлично! Мы успешно передали объект должным образом.
Чтобы упростить код, мы можем использовать std::forward из библиотеки C++ <utility>,

template <typename T>
void PerfectForwardToUseObject(T &&x) {
  UseObject(std::forward<T>(x));
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Надеюсь, что с помощью приведенных выше примеров вы поняли, что подразумевается под идеальной переадресацией.
Как обычно, приведенный выше код доступен с моего github.

Спасибо, что дочитали до конца!

Оцените статью
devanswers.ru
Добавить комментарий