Какова цель перегрузки ссылочной переменной rvalue в std :: forward ()? - PullRequest
15 голосов
/ 29 мая 2019

Я экспериментирую с Perfect Forwarding и обнаружил, что std::forward() требуется две перегрузки:

Номер перегрузки.1:

template <typename T>
inline T&& forward(typename 
std::remove_reference<T>::type& t) noexcept
{
    return static_cast<T&&>(t);
}

Номер перегрузки 2:

template <typename T>
inline T&& forward(typename 
std::remove_reference<T>::type&& t) noexcept
{
    static_assert(!std::is_lvalue_reference<T>::value,
              "Can not forward an rvalue as an lvalue.");
    return static_cast<T&&>(t);
}

Теперь типичный сценарий для совершенной пересылки выглядит примерно так:

template <typename T>
void wrapper(T&& e)
{
    wrapped(forward<T>(e));
}

Конечно, вы знаете, что когдаwrapper() создается, T зависит от того, является ли переданный ему аргумент lvalue или rvalue.Если это lvalue типа U, T выводится в U&.Если это значение r, T выводится в U.

В любом случае - в области действия wrapper() - e - lvalue, поэтому всегда используется первая перегрузка std::forward().

Теперь мой вопрос:

Каков допустимый сценарий, в котором используется (и необходима) 2-я перегрузка?

1 Ответ

14 голосов
/ 29 мая 2019

Обоснование конструкции для forward подробно обсуждается в N2951 .

В этом документе изложены 6 вариантов использования:

A. Должен пересылать lvalue как lvalue. Все реализации проходят этот тест.Но это не классический идеальный образец пересылки.Цель этого теста - показать, что реализация 2 не соответствует своей заявленной цели - предотвратить все варианты использования, кроме идеальной пересылки.

B. Следует переслать rvalue как rvalue. Как и в случае использования A, это преобразование идентичности, и это представляет собой мотивирующий пример, где необходимо преобразование идентичности.

C. Если не переадресовывать значение r как значение l. Этот вариант использования демонстрирует опасную ситуацию случайного создания висячей ссылки.

D. Следует переслать менее квалифицированные cv выражения в более квалифицированные cv выражения. Мотивирующий вариант использования, включающий добавление const во время пересылки.

E. Следует пересылать выражения производного типа в доступный однозначный базовый тип. Мотивирующий вариант использования, включающий пересылку производного типа в базовый тип.

F. Если не пересылать произвольные преобразования типов. Этот пример использования демонстрирует, как произвольные преобразования в прямом переходе приводят к ошибкам повисшего эталонного времени выполнения.

Вторая перегрузка включаетслучаи B и C.

В статье приводятся примеры каждого варианта использования, которые слишком длинны для повторения здесь.

Обновление

Я только что выполнил «решение» только первой перегрузки через эти 6 вариантов использования, и это упражнение показывает, что вторая перегрузка также позволяет использовать вариант F: не следует пересылать произвольные преобразования типов.

...