【问题标题】:Move all arguments to lambda将所有参数移至 lambda
【发布时间】:2016-01-26 09:03:20
【问题描述】:

我想创建可以接受任意数量参数的 lambda,例如:

template <typename... Args>
void f(Args... args) {
    auto l = [args...]() {
        g(args...);
    }
    // use l
}

这里的问题是它不适用于仅移动类型。如果它只有 1 个参数,我会这样做

void f(Arg arg) {
    auto l = [arg = std::move(arg)]() {
        g(move(arg));
    }
}

如何将所有参数移动到 lambda?

【问题讨论】:

    标签: c++ lambda variadic-templates perfect-forwarding


    【解决方案1】:
    template <class... Args>
    void f(Args... args) {
      auto l = [tup=std::make_tuple(std::move(args)...)] {
        std::apply([](auto&&...args){
          g(decltype(args)(args)...);
        }, tup );
      };
    }
    

    有点恶心。

    将它们打包成一个元组,然后用std::apply 解包元组。如果您缺少std::apply,请给自己写一个等效的。

    如果您想使用右值调用 g,请将外部 lambda 设置为可变,并将 move 元组设置为内部 lambda。

    如果您想访问外部参数等,内部 lambda 可以默认捕获 &amp;

    我们甚至可以稍微抽象一下这个模式:

    template<class F, class...Args>
    auto forward_capture( F&& f, Args&&...args ) {
      return [
        f=std::forward<F>(f),
        tup=std::make_tuple(std::forward<Args>(args)...)
      ]{
        return std::apply( f, tup );
      };
    }
    

    使用:

    template <typename... Args>
    void f(Args... args) {
      auto l = forward_capture(
        [](auto&&...args) {
          g(args...);
        },
        std::move(args)...
      );
      // use l
    }
    

    如果你想先捕获列表,我们可以做到:

    template<class...Args>
    auto forward_capture( Args&&...args ) {
      return [
        tup=std::make_tuple(std::forward<Args>(args)...)
      ](auto&& f)mutable{
        return [
          f=decltype(f)(f),
          tup=std::move(tup)
        ]{
          return std::apply( f, tup );
        };
      };
    }
    

    使用:

    template <typename... Args>
    void f(Args... args) {
      auto l = forward_capture(std::move(args)...)(
        [](auto&&...args) {
          g(args...);
        }
      );
      // use l
    }
    

    它的“优势”在于我们有 3 个嵌套的 lambda。

    或者更有趣:

    template<class...Args>
    struct arrow_star {
        std::tuple<Args...> args;
        template<class F>
        auto operator->*(F&& f)&& {
          return [f=std::forward<F>(f),args=std::move(args)]()mutable{
            return std::experimental::apply( std::move(f), std::move(args) );
          };
        }
    };
    template<class...Args>
    arrow_star<std::decay_t<Args>...> forward_capture( Args&&...args ) {
      return {std::make_tuple(std::forward<Args>(args)...)};
    }
    template<class...Args>
    auto f(Args... args)
    {
      return
        forward_capture( std::move(args)... )
        ->*
        [](auto&&...args){
          g(decltype(args)(args)...);
        };
    }
    

    live example.

    【讨论】:

    • @RiaD 我在这里,这里有 2 个变体。 ;)
    • 我有一个关于使用初始化程序捕获的问题。我读到它们主要用于仅移动类型,但除了 [x = std::move(x)] 之外,不能先引用然后移动 -> [&amp;]{ std::move(x); }?在我未经训练的人看来,将它移到捕获列表中似乎是多余的。
    • @0x499 &amp; 具有终身影响。
    猜你喜欢
    • 2018-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-06
    • 2020-10-04
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    相关资源
    最近更新 更多