Вы используете std::apply
, потому что:
1: Реализация apply
, даже если у вас есть доступ к std::invoke
, является большой болью.Превращение кортежа в пакет параметров не является тривиальной операцией.Реализация apply
будет выглядеть примерно так (из cppref):
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::remove_reference_t<Tuple>>>{});
}
Конечно, это не самый сложный код в мире для написания, но он также не совсем тривиален.Особенно, если вы не используете трюк метапрограммирования index_sequence
.
2: потому что вызов функции путем распаковки элементов tuple
довольно полезен.Базовая операция, которую он поддерживает, - это возможность упаковать набор аргументов, передать их и затем вызвать функцию с этими параметрами.Технически у нас уже есть возможность сделать это с одним параметром (передавая значение вокруг), но через apply
вы получаете возможность сделать это с несколькими параметрами.
Это также позволяет вам делатьтрюки метапрограммирования, такие как метапрограммное выполнение маршаллинга между языками.Вы регистрируете функцию в такой системе, которой дается подпись функции (и самой функции).Эта подпись используется для маршалинга данных посредством метапрограммирования.
Когда другой язык вызывает вашу функцию, генерируемая метапрограммой функция просматривает список типов параметров и извлекает значения из другого языка на основе этих типов.,Из чего он их извлекает?Некоторая структура данных, которая содержит значения.А поскольку метапрограммирование не может (легко) создать struct/class
, вы вместо этого строите tuple
(действительно, поддержка метапрограммирования, подобная этой, составляет 80% от того, почему существует tuple
).
Как только tuple<Params>
встроенный, вы используете std::apply
для вызова функции.Вы действительно не можете сделать это с invoke
.
3: Вы не хотите превращать все параметры в tuple
, чтобы иметь возможность выполнить эквивалент invoke
.
4: Вам необходимо установить разницу между invoke
функцией, которая требует tuple
, и apply
, чтобы распаковать tuple
.В конце концов, если вы пишете шаблонную функцию, которая выполняет invoke
для параметров, указанных пользователем, было бы ужасно, если бы пользователь просто предоставил один tuple
в качестве аргумента, а ваша функция invoke
была распакована.it.
Вы можете использовать другие средства для дифференциации случаев, но наличие различных функций является адекватным решением для простого случая.Если бы вы писали более обобщенную функцию apply
-стиля, где вы хотите иметь возможность распаковывать tuple
s в дополнение к передаче других аргументов или распаковывать несколько кортежей в списки аргументов (или их комбинацию), вы быЯ хочу иметь специальный super_invoke
, который мог бы справиться с этим.
Но invoke
- простая функция для простых нужд.То же самое касается apply
.