Как использовать лямбду, чтобы сделать std :: invoke отложенной оценкой? - PullRequest
2 голосов
/ 07 июля 2019

C ++ 11 представляет lambdas , что позволяет нам легче реализовывать отложенные вычисления в C ++, поэтому мне интересно, можно ли сделать std :: invoke таким образом?

Согласно cppreference, std :: invoke реализован с:

template <typename F, typename... Args>
decltype(auto) invoke(F&& f, Args&&... args) 
  noexcept(std::is_nothrow_invocable_v<F, Args...>)
{
    return detail::INVOKE(std::forward<F>(f), std::forward<Args>(args)...);
}

Мы можем видеть много идеальной пересылки здесь, и я хочу сделать эту ленивую оценку.Вот моя реализация:

template <typename F, typename... Args>
constexpr auto delay_invoke(F&& f, Args&&... args) {
    return [&]() -> decltype(auto) {
        return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    };
}

И он прошел мой тест в wandbox.Так как эта лямбда захватывается по ссылке, я думаю, что в такой ленивой оценке есть проблема с привязкой, поэтому я пробую вторую реализацию с захватом по значению:

template <typename F, typename... Args>
constexpr auto delay_invoke(F&& f, Args&&... args) {
    return [=]() mutable -> decltype(auto) {
        return std::invoke(static_cast<F&&>(f), static_cast<Args&&>(args)...);
    };
}

, и она также прошла мой тест.Я прав?У вас есть хорошая реализация или другое хорошее решение?спасибо!

1 Ответ

4 голосов
/ 08 июля 2019

У вас есть хорошая реализация или другое хорошее решение?

Ваши два примера либо фиксируют все по ссылке (что плохо, потому что болтается, особенно в таком случае), либо копируют все (что менее плохо, чем другие один, но просто неэффективный, так как некоторые из аргументов могут быть значениями). То, что вы хотите сделать, это переслать весь пакет. Однако в C ++ 17 захват пакета «вперед» был очень утомительным. К счастью, в C ++ 20 благодаря P0780 это станет намного проще. Мотивирующий пример в этой статье был очень близок вашему примеру.

Для версии C ++ 17 для пересылки вперед требуется tuple (или что-то подобное):

template <typename... Args>
auto delay_invoke(Args&&... args) {
    return [tup=std::make_tuple(std::forward<Args>(args)...)]() mutable -> decltype(auto) {
        return std::apply([](auto&... args) -> decltype(auto) {
            return std::invoke(static_cast<Args&&>(args)...);
        }, tup);
    };
}

Версия C ++ 20 гораздо проще:

template <typename... Args>
auto delay_invoke(Args&&... args) {
    return [...args=std::forward<Args>(args)]() mutable -> decltype(auto) {
        return std::invoke(std::forward<Args>(args)...);
    };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...