Я экспериментирую с 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);
}
Теперь типичный сценарий для Perfect Forwarding выглядит примерно так:
template <typename T>
void wrapper(T&& e)
{
wrapped(forward<T>(e));
}
Конечно, вы знаете, что когда создается экземпляр wrapper()
, T
зависит от того, является ли переданный ему аргумент lvalue или rvalue. Если это lvalue типа U
, T
выводится в U&
. Если это rvalue, T
выводится в U
.
В любом случае - в рамках wrapper()
- e
является lvalue, поэтому всегда используется первая перегрузка std::forward()
.
Теперь мой вопрос:
Каков допустимый сценарий, в котором используется (и необходим) вторая перегрузка?
В случае, когда аргумент не является lvalue. Скажем, std::forward<int>(2)
.
@Raymond Chen: конечно, но я не могу представить, когда такое использование необходимо
Дайте определение «действительному сценарию». Мне кажется, что static_assert
предназначен для предотвращения неправильного использования API, а не для покрытия «допустимого сценария».
Обоснование дизайна forward
подробно обсуждается в N2951.
В этом документе изложены 6 вариантов использования:
A. Should forward an lvalue as an lvalue. All implementations pass this test. But this is not the classic perfect forwarding pattern. The purpose of this test is to show that implementation 2 fails in its stated goal of preventing all use cases except perfect forwarding.
B. Should forward an rvalue as an rvalue. Like use case A, this is an identity transformation and this presents a motivating example where the identity transformation is needed.
C. Should not forward an rvalue as an lvalue. This use case demonstrates a dangerous situation of accidentally creating a dangling reference.
D. Should forward less cv-qualified expressions to more cv-qualified expressions. A motivating use case involving the addition of const during the forward.
E. Should forward expressions of derived type to an accessible, unambiguous base type. A motivating use case involving forwarding a derived type to a base type.
F. Should not forward arbitrary type conversions. This use case demonstrates how arbitrary conversions within a forward lead to dangling reference run time errors.
Вторая перегрузка включает случаи B и C.
Далее в документе приводятся примеры каждого варианта использования, которые слишком длинны, чтобы повторять их здесь.
Обновлять
Я только что провел «решение» только первой перегрузки через эти 6 вариантов использования, и это упражнение показывает, что вторая перегрузка также включает вариант использования F: Не следует перенаправлять преобразования произвольного типа.
лол был на полпути к тому, чтобы напечатать ответ со ссылкой на эту статью - рад, что вы здесь :-)
Этот язык сходит с ума.