reverse_kacejot
@reverse_kacejot
Junior C++ Developer, bachelor of Applied Math

Зачем std::forward иметь две сигнатуры и явно указывать тип?

В стандарте std::forward имеет две сигнатуры (для lvalue и rvalue) и требует от пользователя явно указанного типа:

template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;


В то время, когда тип можно вывести и реализовать всего лишь одну сигнатуру. В static_cast сработает сворачивание ссылок и результат будет эквивалентным стандартной функции.

template <typename T>
decltype(auto) my_forward(T&& arg) // universal reference (can be rvalue- or lvalue-reference)
{
    return static_cast<T&&>(arg);
    // T&& can be expanded to:
    // Foo& && - gives us Foo&
    // const Foo& && - give us const Foo&
    // Foo && - gives us Foo&&
    // const Foo && - gives us const Foo&&
}


Сравнение использования:

template <typename T>
func (T&& foo1, T&& foo2)
{
    f(std::forward<Foo>(foo1), my_forward(foo2));
}
  • Вопрос задан
  • 691 просмотр
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Думаю, вот эта статья даст ответы на твои вопросы.

Если максимально точечно, то это раздел "Реализация идеальной передачи с использованием std::forward" и цитата в нем.
Еще один момент, который нужно отметить: использование std::remove_reference. На самом деле forward может быть реализован и без использования этой функции. Сжатие ссылок выполнит всю работу, таким образом, применение std::remove_reference для этого избыточно. Однако, эта функция позволяет вывести T& t в ситуации, когда этот тип не может быть выведен (согласно стандарту С++, 14.8.2.5), поэтому необходимо явно указывать параметры шаблона при вызове std::forward.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
makarenya
@makarenya
программист
Попытка №2, в первой был бред:
Основной проблемой является то, что внутри метода все параметры имеют тип l-value. То есть модификатор параметра && - это лишь требование, чтобы на место этого аргумента вызывающий код передал r-value (а для шаблонов даже этого не требует, благодаря склейки ссылок). И раз так, то ваш метод my_forward переделает вернёт l-value ссылки вообще для всех аргументов.

Отсюда берётся необходимость явно указывать тип аргумента - чтобы использовался именно выведенный тип, а не l-value на этот тип. Ну и почему 2 специализации: если указать явно тип аргумента и явно специализировать функцию каким-нибудь r-value, то my_forward будет ожидать строго r-value, и являющееся l-value значение параметра туда не подойдёт.

В объяснение моих мыслей, код
#include <iostream>

template<typename T>
decltype(auto) my_forward(T&& arg)
{
    return static_cast<T&&>(arg);
}

template <typename T, typename K>
void forward(T&& t, K&& k)
{
    std::cout << std::is_same<int&&, decltype(my_forward(t))>::value << std::endl;
    std::cout << std::is_same<int&, decltype(my_forward(k))>::value << std::endl;
    std::cout << std::is_same<int&&, decltype(std::forward<T>(t))>::value << std::endl;
    std::cout << std::is_same<int&, decltype(std::forward<K>(t))>::value << std::endl;
}

int main()
{
    int lval = 10;
    forward(10, lval);
    return 0;
}


вернёт
0
1
1
1

То есть предположение что my_forward вернуло int&& для первого параметра - не верно!
Ответ написан
@Mercury13
Программист на «си с крестами» и не только
std::forward предназначен для использования в шаблонах; без шаблона можно использовать старый добрый std::move.
Кроме того, std::forward налажен таким образом, что шаблонный параметр надо указывать явно.

Задача std::forward — передать далее простую или временную ссылку в зависимости от того, какую ссылку передаёт пользователь.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы