Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/150.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ c++;lambdas如何从上层范围捕获可变参数包_C++_Lambda_C++14_Variadic Templates_Generic Lambda - Fatal编程技术网

C++ c++;lambdas如何从上层范围捕获可变参数包

C++ c++;lambdas如何从上层范围捕获可变参数包,c++,lambda,c++14,variadic-templates,generic-lambda,C++,Lambda,C++14,Variadic Templates,Generic Lambda,我研究了通用的lambdas,稍微修改了示例, 因此,我的lambda应该捕获上lambda的可变参数包。 因此,基本上,作为(auto&&…)提供给上lambda的内容应该以某种方式被捕获到[=]块中 (完美的转发是另一个问题,我很好奇这里有可能吗?) #包括 #包括 #包括 //基本情况 void doPrint(std::ostream&out){} 模板 void doPrint(std::ostream&out、T&T、Args&;Args) { 出来 完美的转发是另一个问题,我

我研究了通用的lambdas,稍微修改了示例, 因此,我的lambda应该捕获上lambda的可变参数包。 因此,基本上,作为
(auto&&…
)提供给上lambda的内容应该以某种方式被捕获到
[=]
块中

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

#包括
#包括
#包括
//基本情况
void doPrint(std::ostream&out){}
模板
void doPrint(std::ostream&out、T&T、Args&;Args)
{
出来
完美的转发是另一个问题,我很好奇这里有可能吗

#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"

}
嗯……在我看来,完美的转发是个问题

ts..
的捕获效果很好,如果您在内部lambda中进行更改

printer(std::forward<decltype(ts)>(ts)...);
程序可以编译

问题是通过值捕获
ts..
(使用
[=]
)它们变成
常量
值,
打印机()
(即接收
自动和…变量
的lambda)接收引用(
&
&

您可以在以下函数中看到相同的问题

void bar (int &&)
 { }

void foo (int const & i)
 { bar(std::forward<decltype(i)>(i)); }
void条(int&&)
{ }
void foo(内部常量和一)
{bar(std::forward(i));}
从叮当声中我得到了

tmp_003-14,gcc,clang.cpp:21:4: error: no matching function for call to 'bar'
 { bar(std::forward<decltype(i)>(i)); }
   ^~~
tmp_003-14,gcc,clang.cpp:17:6: note: candidate function not viable: 1st argument
      ('const int') would lose const qualifier
void bar (int &&)
     ^
tmp_003-14,gcc,clang.cpp:21:4:错误:调用“bar”时没有匹配函数
{bar(std::forward(i));}
^~~
tmp_003-14,gcc,clang.cpp:17:6:注:候选函数不可行:第一个参数
('const int')将丢失常量限定符
空栏(int&)
^

解决问题的另一种方法是将
ts..
捕获为引用(因此
[&]
)而不是值。

在C++20中完美捕获

template <typename ... Args>
auto f(Args&& ... args){
    return [... args = std::forward<Args>(args)]{
        // use args
    };
}
通过函数
capture\u调用
来简化解决方案可能是有用的:

#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 <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\u call
通过值捕获变量。完美的方法是尽可能使用移动构造函数。下面是一个C++17代码示例,以便于更好地理解:

#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";
}

捕获值的类型在
capture\u调用
版本中包含
&&
,因为我们必须通过引用访问内部元组中的值,而支持语言的捕获支持直接访问值。

而不是使用
std::tuple
std::apply
,这会使代码混乱不堪,您可以使用
std::bind
将变量参数绑定到lambda(对于C++20之前的解决方案):

模板
自动f(参数&&…参数){
auto functional=[](auto&&…args){/*lambda body*/};
返回std::bind(std::move(functional),std::forward(args)…);
}

旁白:您可能不想两次转发同一个包我对C++17解决方案(使用std::make_tuple()和std::apply()的解决方案)有点困惑。首先,我希望make_tuple()创建的元组包含std::decage-ed类型,但std::reference_-wrapper类型的参数除外。此外,std::apply()将元组转发到std::get(),我相信如果我们将左值引用传递给元组或右值引用,它将返回左值引用。因此,总的来说,在捕获过程中,我们似乎没有完美地转发参数包。我缺少什么?@fireboot捕获的对象作为值存储在lambda对象中。“完美”捕获引用这些值的构造是通过完美转发完成的。应用程序将始终为您提供对捕获的(存储在元组中)的右值引用值,这相当于定义了可变值的C++20版本lambda。为了更好地理解,我添加了一个示例。多亏了您,我在最初的capture_调用中发现了一个错误!以前的第二个make_元组现在被第二个应用程序所取代,该应用程序创建了对捕获值的右值引用“元组视图”。为什么要make_元组,而不是转发_tuple?C++20解决方案不也需要
可变的
?并且
Args&&Args
应该是
Args&&Args
@Mr.Wonko您的
是对的,
,我修正了它。谢谢!您是否需要
可变的
取决于闭包中的值是否应该是
常量
。所以,如果您你想要旧的解决方法的准确行为,那么是的。
#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
}
#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))
        ));
    };
}
#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()