Как std :: применять параметры forward без явного std :: forward?

Рассмотреть возможность реализации std::apply:

namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}
}  // namespace detail

template <class F, class Tuple>
constexpr decltype(auto) apply(F &&f, Tuple &&t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}

Почему при вызове функции (f) с набором параметров для передачи (t) нам не нужно выполнять std::forward на каждом элементе кортежа std::get<I>(std::forward<Tuple>(t))... в реализации?

9

Решение

Тебе не нужно std::forward каждый элемент, потому что std::get перегружен для rvalue-reference и lvalue-reference кортежа.

std::forward<Tuple>(t) даст вам либо lvalue (Tuple &) или значение (Tuple &&) и в зависимости от того, что вы получите, std::get даст вам T & (lvalue) или T && (Rvalue). Смотрите различные перегрузки std::get.


Немного подробностей о std::tuple а также std::get

Как упомянуто рассказчик, каждый член кортежа является lvalue, вне зависимости от того, был ли он составлен из rvalue или lvalue:

double a{0.0};
auto t1 = std::make_tuple(int(), a);
auto t2 = std::make_tuple(int(), double());

Вопрос в том, является ли кортеж значимым? Если да, вы можете переместить его участника, если нет, вы должны сделать копию, но std::get уже позаботиться об этом, вернув члена с соответствующей категорией.

decltype(auto) a1 = std::get<0>(t1);
decltype(auto) a2 = std::get<0>(std::move(t1));

static_assert(std::is_same<decltype(a1), int&>{}, "");
static_assert(std::is_same<decltype(a2), int&&>{}, "");

Вернуться к конкретному примеру с std::forward:

template <typename Tuple>
void f(Tuple &&tuple) { // tuple is a forwarding reference
decltype(auto) a = std::get<0>(std::forward<Tuple>(tuple));
}

f(std::make_tuple(int())); // Call f<std::tuple<int>>(std::tuple<int>&&);
std::tuple<int> t1;
f(t1); // Call f<std::tuple<int>&>(std::tuple<int>&);

В первом звонке f, тип a будет int&& так как tuple будет переслан как std::tuple<int>&&в то время как во втором случае его тип будет int& так как tuple будет переслан как std::tuple<int>&,

7

Другие решения

std::forward используется, чтобы убедиться, что все поступает на сайт вызова с правильной категорией значения.

Но каждый член кортежа является lvalue, даже если это кортеж rvalue Рекомендации.

2

По вопросам рекламы [email protected]