Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.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++ 在lambda中移动捕获_C++_Lambda_C++11_Rvalue Reference - Fatal编程技术网

C++ 在lambda中移动捕获

C++ 在lambda中移动捕获,c++,lambda,c++11,rvalue-reference,C++,Lambda,C++11,Rvalue Reference,如何在C++11 lambda中通过移动(也称为右值引用)进行捕获 我试着写这样的东西: std::unique_ptr<int> myPointer(new int); std::function<void(void)> example = [std::move(myPointer)]{ *myPointer = 4; }; std::unique_ptr<int> myPointer(new int{42}); auto lambda = st

如何在C++11 lambda中通过移动(也称为右值引用)进行捕获

我试着写这样的东西:

std::unique_ptr<int> myPointer(new int);

std::function<void(void)> example = [std::move(myPointer)]{
   *myPointer = 4;
};
std::unique_ptr<int> myPointer(new int{42});

auto lambda = std::bind([](std::unique_ptr<int>& myPointerArg){
    *myPointerArg = 4;
     myPointerArg.reset(new int{237});
}, std::move(myPointer));
std::unique_ptr myPointer(新int);
std::函数示例=[std::move(myPointer)]{
*myPointer=4;
};

您还可以使用
std::bind
捕获
唯一的\u ptr

std::function<void()> f = std::bind(
                              [] (std::unique_ptr<int>& p) { *p=4; },
                              std::move(myPointer)
                          );
std::function f=std::bind(
[](std::unique_ptr&p){*p=4;},
标准::移动(myPointer)
);
C++14中的广义lambda捕获 在C++14中,我们将使用所谓的。这将启用移动捕获。以下是C++14中的法律代码:

using namespace std;

// a unique_ptr is move-only
auto u = make_unique<some_type>( some, parameters );  

// move the unique_ptr into the lambda
go.run( [ u = move(u) ] { do_something_with( u ); } ); 
在C++11中,这还不可能实现,但有一些技巧涉及助手类型。幸运的是,Clang3.4编译器已经实现了这个令人敬畏的特性。该编译器将于2013年12月或2014年1月发布,前提是保留该版本

更新:于2014年1月6日发布了上述功能

移动捕获的解决方法 下面是一个帮助函数
make\u rref
的实现,它有助于人工移动捕捉

#include <cassert>
#include <memory>
#include <utility>

template <typename T>
struct rref_impl
{
    rref_impl() = delete;
    rref_impl( T && x ) : x{std::move(x)} {}
    rref_impl( rref_impl & other )
        : x{std::move(other.x)}, isCopied{true}
    {
        assert( other.isCopied == false );
    }
    rref_impl( rref_impl && other )
        : x{std::move(other.x)}, isCopied{std::move(other.isCopied)}
    {
    }
    rref_impl & operator=( rref_impl other ) = delete;
    T && move()
    {
        return std::move(x);
    }

private:
    T x;
    bool isCopied = false;
};

template<typename T> rref_impl<T> make_rref( T && x )
{
    return rref_impl<T>{ std::move(x) };
}
这里的
lambda
是一个函子对象(几乎是真实的lambda),它在传递给
capture()
时捕获了
std::move(p)
capture
的第二个参数是lambda,它将捕获的变量作为参数。当
lambda
用作函数对象时,传递给它的所有参数将作为捕获变量后的参数转发给内部lambda。(在我们的案例中,没有进一步的论据需要转发)。本质上,与前面的解决方案相同。以下是如何实现
capture

#include <utility>

template <typename T, typename F>
class capture_impl
{
    T x;
    F f;
public:
    capture_impl( T && x, F && f )
        : x{std::forward<T>(x)}, f{std::forward<F>(f)}
    {}

    template <typename ...Ts> auto operator()( Ts&&...args )
        -> decltype(f( x, std::forward<Ts>(args)... ))
    {
        return f( x, std::forward<Ts>(args)... );
    }

    template <typename ...Ts> auto operator()( Ts&&...args ) const
        -> decltype(f( x, std::forward<Ts>(args)... ))
    {
        return f( x, std::forward<Ts>(args)... );
    }
};

template <typename T, typename F>
capture_impl<T,F> capture( T && x, F && f )
{
    return capture_impl<T,F>(
        std::forward<T>(x), std::forward<F>(f) );
}
#包括
模板
类捕获\u impl
{
tx;
F;
公众:
捕获执行(T&x、F&F)
:x{std::forward(x)},f{std::forward(f)}
{}
模板自动运算符()(Ts&…args)
->decltype(f(x,std::forward(args)…)
{
返回f(x,std::forward(args)…);
}
模板自动运算符()(Ts&…args)常量
->decltype(f(x,std::forward(args)…)
{
返回f(x,std::forward(args)…);
}
};
模板
捕获\u impl捕获(T&&x、F&&F)
{
返回捕获\u impl(
标准:正向(x),标准:正向(f);
}

第二个解决方案也更干净,因为如果捕获的类型不可复制,它将禁用复制lambda。在第一个解决方案中,只能在运行时使用
assert()

进行检查,您可以使用它来实现大部分需要的功能,如下所示:

std::unique_ptr<int> myPointer(new int);

std::function<void(void)> example = [std::move(myPointer)]{
   *myPointer = 4;
};
std::unique_ptr<int> myPointer(new int{42});

auto lambda = std::bind([](std::unique_ptr<int>& myPointerArg){
    *myPointerArg = 4;
     myPointerArg.reset(new int{237});
}, std::move(myPointer));
但是,这段代码并不能为您购买任何在C++11中没有的东西。(在某些情况下,广义lambda捕获功能更强大,但在本例中并非如此。)

现在只有一个问题;您想将此函数放入,但该类要求该函数为,但它不是,这只是因为它存储了一个
std::unique\u ptr
,而不是

您需要使用包装器类和另一个间接级别来解决这个问题,但可能根本不需要。根据您的需要,您可以使用;它的工作与相同,但它不要求该函数是可复制的,只能移动(类似地,只能移动)。缺点是,因为它打算与std::future一起使用,所以只能调用一次

下面是一个简短的程序,展示了所有这些概念

#include <functional>   // for std::bind
#include <memory>       // for std::unique_ptr
#include <utility>      // for std::move
#include <future>       // for std::packaged_task
#include <iostream>     // printing
#include <type_traits>  // for std::result_of
#include <cstddef>

void showPtr(const char* name, const std::unique_ptr<size_t>& ptr)
{
    std::cout << "- &" << name << " = " << &ptr << ", " << name << ".get() = "
              << ptr.get();
    if (ptr)
        std::cout << ", *" << name << " = " << *ptr;
    std::cout << std::endl;
}

// If you must use std::function, but your function is MoveConstructable
// but not CopyConstructable, you can wrap it in a shared pointer.
template <typename F>
class shared_function : public std::shared_ptr<F> {
public:
    using std::shared_ptr<F>::shared_ptr;

    template <typename ...Args>
    auto operator()(Args&&...args) const
        -> typename std::result_of<F(Args...)>::type
    {
        return (*(this->get()))(std::forward<Args>(args)...);
    }
};

template <typename F>
shared_function<F> make_shared_fn(F&& f)
{
    return shared_function<F>{
        new typename std::remove_reference<F>::type{std::forward<F>(f)}};
}


int main()
{
    std::unique_ptr<size_t> myPointer(new size_t{42});
    showPtr("myPointer", myPointer);
    std::cout << "Creating lambda\n";

#if __cplusplus == 201103L // C++ 11

    // Use std::bind
    auto lambda = std::bind([](std::unique_ptr<size_t>& myPointerArg){
        showPtr("myPointerArg", myPointerArg);  
        *myPointerArg *= 56;                    // Reads our movable thing
        showPtr("myPointerArg", myPointerArg);
        myPointerArg.reset(new size_t{*myPointerArg * 237}); // Writes it
        showPtr("myPointerArg", myPointerArg);
    }, std::move(myPointer));

#elif __cplusplus > 201103L // C++14

    // Use generalized capture
    auto lambda = [myPointerCapture = std::move(myPointer)]() mutable {
        showPtr("myPointerCapture", myPointerCapture);
        *myPointerCapture *= 56;
        showPtr("myPointerCapture", myPointerCapture);
        myPointerCapture.reset(new size_t{*myPointerCapture * 237});
        showPtr("myPointerCapture", myPointerCapture);
    };

#else
    #error We need C++11
#endif

    showPtr("myPointer", myPointer);
    std::cout << "#1: lambda()\n";
    lambda();
    std::cout << "#2: lambda()\n";
    lambda();
    std::cout << "#3: lambda()\n";
    lambda();

#if ONLY_NEED_TO_CALL_ONCE
    // In some situations, std::packaged_task is an alternative to
    // std::function, e.g., if you only plan to call it once.  Otherwise
    // you need to write your own wrapper to handle move-only function.
    std::cout << "Moving to std::packaged_task\n";
    std::packaged_task<void()> f{std::move(lambda)};
    std::cout << "#4: f()\n";
    f();
#else
    // Otherwise, we need to turn our move-only function into one that can
    // be copied freely.  There is no guarantee that it'll only be copied
    // once, so we resort to using a shared pointer.
    std::cout << "Moving to std::function\n";
    std::function<void()> f{make_shared_fn(std::move(lambda))};
    std::cout << "#4: f()\n";
    f();
    std::cout << "#5: f()\n";
    f();
    std::cout << "#6: f()\n";
    f();
#endif
}
您可以看到堆位置被重用,这表明
std::unique\u ptr
工作正常。当我们将函数隐藏在一个包装器中时,您还可以看到函数本身在移动,我们将其馈送到
std::function

如果我们切换到使用
std::packaged_task
,它将成为最后一部分

Moving to std::packaged_task
#4: f()
- &myPointerArg = 0xbfffe590, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 751631360
- &myPointerArg = 0xbfffe590, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 3436650496
- &myPointerArg = 0xbfffe590, myPointerArg.get() = 0x7ae3d000, *myPointerArg = 2737348608
因此,我们看到函数已被移动,但它并没有移动到堆中,而是在堆栈上的
std::packaged_任务


希望这有帮助

我在看这些答案,但我发现bind很难阅读和理解。所以我所做的是创建一个类,该类在copy上移动。通过这种方式,它对它正在做的事情是明确的

#include <iostream>
#include <memory>
#include <utility>
#include <type_traits>
#include <functional>

namespace detail
{
    enum selection_enabler { enabled };
}

#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), ::detail::selection_enabler> \
                          = ::detail::enabled

// This allows forwarding an object using the copy constructor
template <typename T>
struct move_with_copy_ctor
{
    // forwarding constructor
    template <typename T2
        // Disable constructor for it's own type, since it would
        // conflict with the copy constructor.
        , ENABLE_IF(
            !std::is_same<std::remove_reference_t<T2>, move_with_copy_ctor>::value
        )
    >
    move_with_copy_ctor(T2&& object)
        : wrapped_object(std::forward<T2>(object))
    {
    }

    // move object to wrapped_object
    move_with_copy_ctor(T&& object)
        : wrapped_object(std::move(object))
    {
    }

    // Copy constructor being used as move constructor.
    move_with_copy_ctor(move_with_copy_ctor const& object)
    {
        std::swap(wrapped_object, const_cast<move_with_copy_ctor&>(object).wrapped_object);
    }

    // access to wrapped object
    T& operator()() { return wrapped_object; }

private:
    T wrapped_object;
};


template <typename T>
move_with_copy_ctor<T> make_movable(T&& object)
{
    return{ std::forward<T>(object) };
}

auto fn1()
{
    std::unique_ptr<int, std::function<void(int*)>> x(new int(1)
                           , [](int * x)
                           {
                               std::cout << "Destroying " << x << std::endl;
                               delete x;
                           });
    return [y = make_movable(std::move(x))]() mutable {
        std::cout << "value: " << *y() << std::endl;
        return;
    };
}

int main()
{
    {
        auto x = fn1();
        x();
        std::cout << "object still not deleted\n";
        x();
    }
    std::cout << "object was deleted\n";
}
那么,指针地址可能会有所不同。;)

很晚了,但由于有些人(包括我)仍然坚持使用c++11:

老实说,我真的不喜欢任何张贴的解决方案。我相信它们会起作用,但它们需要很多额外的东西和/或神秘的
std::bind
语法。。。我认为,在升级到C++ >=14时,无论如何都不需要为这样一个临时解决方案付出努力。因此,我认为最好的解决方案是完全避免c++11的移动捕获

通常最简单、可读性最好的解决方案是使用
std::shared_ptr
,它是可复制的,因此完全可以避免移动。缺点是,它的效率稍低,但在许多情况下,效率并不是那么重要

// myPointer could be a parameter or something
std::unique_ptr<int> myPointer(new int);

// convert/move the unique ptr into a shared ptr
std::shared_ptr<int> mySharedPointer( std::move(myPointer) );

std::function<void(void)> = [mySharedPointer](){
   *mySharedPointer = 4;
};

// at end of scope the original mySharedPointer is destroyed,
// but the copy still lives in the lambda capture.

是的,这些天来,原始指针是非常不受欢迎的(而且不是没有理由的),但我真的认为在这些罕见的(和暂时的!)情况下,它们是最好的解决方案。

这似乎对gcc4.8有效

#include <memory>
#include <iostream>

struct Foo {};

void bar(std::unique_ptr<Foo> p) {
    std::cout << "bar\n";
}

int main() {
    std::unique_ptr<Foo> p(new Foo);
    auto f = [ptr = std::move(p)]() mutable {
        bar(std::move(ptr));
    };
    f();
    return 0;
}
#包括
#包括
结构Foo{};
空栏(标准::唯一的\u ptr p){

std::我可以在G++-4.8-std=c++11上使用这么长时间吗?我还以为这是c++11的功能。现在我习惯了使用它,突然意识到它是c++14的功能…我该怎么办!!@RnMss你指的是什么功能?广义的lambda捕获?@RalphTandetzky我想是的,我刚刚检查了一下,与XCode捆绑的clang版本似乎是正确的也支持它!它给出了一个警告,说明它是C++1y扩展,但确实有效。@RNMS要么使用
moveCapture
包装器将它们作为参数传递(此方法在上面和Capn'Proto中使用,这是Protobuff的创建者创建的库),要么接受您需要支持它的编译器:PNo,它实际上是
#include <iostream>
#include <memory>
#include <utility>
#include <type_traits>
#include <functional>

namespace detail
{
    enum selection_enabler { enabled };
}

#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), ::detail::selection_enabler> \
                          = ::detail::enabled

// This allows forwarding an object using the copy constructor
template <typename T>
struct move_with_copy_ctor
{
    // forwarding constructor
    template <typename T2
        // Disable constructor for it's own type, since it would
        // conflict with the copy constructor.
        , ENABLE_IF(
            !std::is_same<std::remove_reference_t<T2>, move_with_copy_ctor>::value
        )
    >
    move_with_copy_ctor(T2&& object)
        : wrapped_object(std::forward<T2>(object))
    {
    }

    // move object to wrapped_object
    move_with_copy_ctor(T&& object)
        : wrapped_object(std::move(object))
    {
    }

    // Copy constructor being used as move constructor.
    move_with_copy_ctor(move_with_copy_ctor const& object)
    {
        std::swap(wrapped_object, const_cast<move_with_copy_ctor&>(object).wrapped_object);
    }

    // access to wrapped object
    T& operator()() { return wrapped_object; }

private:
    T wrapped_object;
};


template <typename T>
move_with_copy_ctor<T> make_movable(T&& object)
{
    return{ std::forward<T>(object) };
}

auto fn1()
{
    std::unique_ptr<int, std::function<void(int*)>> x(new int(1)
                           , [](int * x)
                           {
                               std::cout << "Destroying " << x << std::endl;
                               delete x;
                           });
    return [y = make_movable(std::move(x))]() mutable {
        std::cout << "value: " << *y() << std::endl;
        return;
    };
}

int main()
{
    {
        auto x = fn1();
        x();
        std::cout << "object still not deleted\n";
        x();
    }
    std::cout << "object was deleted\n";
}
value: 1 object still not deleted value: 1 Destroying 000000DFDD172280 object was deleted
// myPointer could be a parameter or something
std::unique_ptr<int> myPointer(new int);

// convert/move the unique ptr into a shared ptr
std::shared_ptr<int> mySharedPointer( std::move(myPointer) );

std::function<void(void)> = [mySharedPointer](){
   *mySharedPointer = 4;
};

// at end of scope the original mySharedPointer is destroyed,
// but the copy still lives in the lambda capture.
// myPointer could be a parameter or something
std::unique_ptr<int> myPointer(new int);

//FIXME:c++11 upgrade to new move capture on c++>=14

// "move" the pointer into a raw pointer
int* myRawPointer = myPointer.release();

// capture the raw pointer as a copy.
std::function<void(void)> = [myRawPointer](){
   std::unique_ptr<int> capturedPointer(myRawPointer);
   *capturedPointer = 4;
};

// ensure that the pointer's value is not accessible anymore after capturing
myRawPointer = nullptr;
#include <memory>
#include <iostream>

struct Foo {};

void bar(std::unique_ptr<Foo> p) {
    std::cout << "bar\n";
}

int main() {
    std::unique_ptr<Foo> p(new Foo);
    auto f = [ptr = std::move(p)]() mutable {
        bar(std::move(ptr));
    };
    f();
    return 0;
}