C ++ Lambdas如何从上限范围捕获可变参数包 [英] c++ lambdas how to capture variadic parameter pack from the upper scope

查看:138
本文介绍了C ++ Lambdas如何从上限范围捕获可变参数包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我研究了通用lambda,并稍加修改了示例
,以便我的lambda可以捕获上lambda的可变参数包。
因此,基本上给上层lambda作为(auto&& ...)-应该以某种方式捕获在 [=]中块。

I study the generic lambdas, and slightly modified the example, so my lambda should capture the upper lambda's variadic parameter pack. So basically what is given to upper lambda as (auto&&...) - should be somehow captured in [=] block.

(完美的转发是另一个问题,我很好奇这有可能吗?)

(The perfect forwarding is another question, I'm curious is it possible here at all?)

#include <iostream>
#include<type_traits>
#include<utility>


// base case
void doPrint(std::ostream& out) {}

template <typename T, typename... Args>
void doPrint(std::ostream& out, T && t, Args && ... args)
{
    out << t << " ";                // add comma here, see below
    doPrint(out, std::forward<Args&&>(args)...);
}

int main()
{
    // generic lambda, operator() is a template with one parameter
    auto vglambda = [](auto printer) {
        return [=](auto&&... ts) // generic lambda, ts is a parameter pack
        {
            printer(std::forward<decltype(ts)>(ts)...);
            return [=] {  // HOW TO capture the variadic ts to be accessible HERE ↓
                printer(std::forward<decltype(ts)>(ts)...); // ERROR: no matchin function call to forward
            }; // nullary lambda (takes no parameters)
        };
    };
    auto p = vglambda([](auto&&...vars) {
        doPrint(std::cout, std::forward<decltype(vars)>(vars)...);
    });
    auto q = p(1, 'a', 3.14,5); // outputs 1a3.14

    //q(); //use the returned lambda "printer"

}


推荐答案

在C ++ 20中完美捕获

template <typename ... Args>
auto f(Args&& ... args){
    return [... args = std::forward<Args>(args)]{
        // use args
    };
}






C + +17和C ++ 14解决方法

在C ++ 17中,我们可以对元组使用解决方法:

In C++17 we can use a workaround with tuples:

template <typename ... Args>
auto f(Args&& ... args){
    return [args = std::make_tuple(std::forward<Args>(args) ...)]()mutable{
        return std::apply([](auto&& ... args){
            // use args
        }, std::move(args));
    };
}

不幸的是 std :: apply 是C ++ 17,在C ++ 14中,您可以自己实现它,也可以使用 boost :: hana

Unfortunately std::apply is C++17, in C++14 you can implement it yourself or do something similar with boost::hana:

namespace hana = boost::hana;

template <typename ... Args>
auto f(Args&& ... args){
    return [args = hana::make_tuple(std::forward<Args>(args) ...)]()mutable{
        return hana::unpack(std::move(args), [](auto&& ... args){
            // use args
        });
    };
}

通过功能 capture_call :

#include <tuple>

// Capture args and add them as additional arguments
template <typename Lambda, typename ... Args>
auto capture_call(Lambda&& lambda, Args&& ... args){
    return [
        lambda = std::forward<Lambda>(lambda),
        capture_args = std::make_tuple(std::forward<Args>(args) ...)
    ](auto&& ... original_args)mutable{
        return std::apply([&lambda](auto&& ... args){
            lambda(std::forward<decltype(args)>(args) ...);
        }, std::tuple_cat(
            std::forward_as_tuple(original_args ...),
            std::apply([](auto&& ... args){
                return std::forward_as_tuple< Args ... >(
                    std::move(args) ...);
            }, std::move(capture_args))
        ));
    };
}

像这样使用:

#include <iostream>

// returns a callable object without parameters
template <typename ... Args>
auto f1(Args&& ... args){
    return capture_call([](auto&& ... args){
        // args are perfect captured here
        // print captured args via C++17 fold expression
        (std::cout << ... << args) << '\n';
    }, std::forward<Args>(args) ...);
}

// returns a callable object with two int parameters
template <typename ... Args>
auto f2(Args&& ... args){
    return capture_call([](int param1, int param2, auto&& ... args){
        // args are perfect captured here
        std::cout << param1 << param2;
        (std::cout << ... << args) << '\n';
    }, std::forward<Args>(args) ...);
}

int main(){
    f1(1, 2, 3)();     // Call lambda without arguments
    f2(3, 4, 5)(1, 2); // Call lambda with 2 int arguments
}






这是 capture_call 的C ++ 14实现:


Here is a C++14 implementation of capture_call:

#include <tuple>

// Implementation detail of a simplified std::apply from C++17
template < typename F, typename Tuple, std::size_t ... I >
constexpr decltype(auto)
apply_impl(F&& f, Tuple&& t, std::index_sequence< I ... >){
    return static_cast< F&& >(f)(std::get< I >(static_cast< Tuple&& >(t)) ...);
}

// Implementation of a simplified std::apply from C++17
template < typename F, typename Tuple >
constexpr decltype(auto) apply(F&& f, Tuple&& t){
    return apply_impl(
        static_cast< F&& >(f), static_cast< Tuple&& >(t),
        std::make_index_sequence< std::tuple_size<
            std::remove_reference_t< Tuple > >::value >{});
}

// Capture args and add them as additional arguments
template <typename Lambda, typename ... Args>
auto capture_call(Lambda&& lambda, Args&& ... args){
    return [
        lambda = std::forward<Lambda>(lambda),
        capture_args = std::make_tuple(std::forward<Args>(args) ...)
    ](auto&& ... original_args)mutable{
        return ::apply([&lambda](auto&& ... args){
            lambda(std::forward<decltype(args)>(args) ...);
        }, std::tuple_cat(
            std::forward_as_tuple(original_args ...),
            ::apply([](auto&& ... args){
                return std::forward_as_tuple< Args ... >(
                    std::move(args) ...);
            }, std::move(capture_args))
        ));
    };
}






capture_call 按值捕获变量。完美的意思是,如果可能,使用move构造函数。以下是一个C ++ 17代码示例,可用于更好地理解:


capture_call captures variables by value. The perfect means that the move constructor is used if possible. Here is a C++17 code example for better understanding:

#include <tuple>
#include <iostream>
#include <boost/type_index.hpp>


// Capture args and add them as additional arguments
template <typename Lambda, typename ... Args>
auto capture_call(Lambda&& lambda, Args&& ... args){
    return [
        lambda = std::forward<Lambda>(lambda),
        capture_args = std::make_tuple(std::forward<Args>(args) ...)
    ](auto&& ... original_args)mutable{
        return std::apply([&lambda](auto&& ... args){
            lambda(std::forward<decltype(args)>(args) ...);
        }, std::tuple_cat(
            std::forward_as_tuple(original_args ...),
            std::apply([](auto&& ... args){
                return std::forward_as_tuple< Args ... >(
                    std::move(args) ...);
            }, std::move(capture_args))
        ));
    };
}

struct A{
    A(){
        std::cout << "  A::A()\n";
    }

    A(A const&){
        std::cout << "  A::A(A const&)\n";
    }

    A(A&&){
        std::cout << "  A::A(A&&)\n";
    }

    ~A(){
        std::cout << "  A::~A()\n";
    }
};

int main(){
    using boost::typeindex::type_id_with_cvr;

    A a;
    std::cout << "create object end\n\n";

    [b = a]{
        std::cout << "  type of the capture value: "
          << type_id_with_cvr<decltype(b)>().pretty_name()
          << "\n";
    }();
    std::cout << "value capture end\n\n";

    [&b = a]{
        std::cout << "  type of the capture value: "
          << type_id_with_cvr<decltype(b)>().pretty_name()
          << "\n";
    }();
    std::cout << "reference capture end\n\n";

    [b = std::move(a)]{
        std::cout << "  type of the capture value: "
          << type_id_with_cvr<decltype(b)>().pretty_name()
          << "\n";
    }();
    std::cout << "perfect capture end\n\n";

    [b = std::move(a)]()mutable{
        std::cout << "  type of the capture value: "
          << type_id_with_cvr<decltype(b)>().pretty_name()
          << "\n";
    }();
    std::cout << "perfect capture mutable lambda end\n\n";

    capture_call([](auto&& b){
        std::cout << "  type of the capture value: "
          << type_id_with_cvr<decltype(b)>().pretty_name()
          << "\n";
    }, std::move(a))();
    std::cout << "capture_call perfect capture end\n\n";
}

输出:

  A::A()
create object end

  A::A(A const&)
  type of the capture value: A const
  A::~A()
value capture end

  type of the capture value: A&
reference capture end

  A::A(A&&)
  type of the capture value: A const
  A::~A()
perfect capture end

  A::A(A&&)
  type of the capture value: A
  A::~A()
perfect capture mutable lambda end

  A::A(A&&)
  type of the capture value: A&&
  A::~A()
capture_call perfect capture end

  A::~A()

捕获值的类型在 capture_call & $ c>版本,因为我们必须通过引用访问内部元组中的值,而语言支持的捕获则支持直接访问该值。

The type of the capture value contains && in the capture_call version because we have to access the value in the internal tuple via reference, while a language supported capture supports direct access to the value.

这篇关于C ++ Lambdas如何从上限范围捕获可变参数包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆