【问题标题】:How to capture std::unique_ptr "by move" for a lambda in std::for_each如何在 std::for_each 中“通过移动”为 lambda 捕获 std::unique_ptr
【发布时间】:2012-05-04 02:06:27
【问题描述】:

我正在学习 c++11 中的新功能并遇到了这个问题。我想通过将其移动到 lambda 中作为 for_each 的参数来捕获 unique_ptr。

设置:

std::array<int,4> arr = {1,3,5,6};
std::unique_ptr<int> p(new int);  (*p) = 3;

尝试 1 - 不起作用,因为 unique_ptr 没有复制构造函数。 c++0x 没有指定 pass by move 语法。

std::for_each(arr.begin(), arr.end(), [p](int& i) { i+=*p; });

尝试 2 - 使用 bind 将 p 的移动副本绑定到采用 int& 的函数:

std::for_each(arr.begin(), arr.end(),
     std::bind([](const unique_ptr<int>& p, int& i){
          i += (*p);
     }, std::move(p))
);

编译器抱怨'result' : symbol is neither a class template nor a function template.

本练习的主要目的是了解如何在 lambda 中捕获可移动变量以供以后使用。

【问题讨论】:

  • 这个问题已经被问过here。但是,我不会投票结束,因为您的问题包含替代(虽然不起作用)解决方案。
  • 另见here。

标签: c++ lambda c++11


【解决方案1】:

更新:从 C++14 开始,您可以在 lambda 中捕获可移动变量。

std::for_each(arr.begin(), arr.end(), [p=std::move(p)](int& i) { i+=*p; });

在 C++11 中,您无法以任何直接的方式将可移动变量捕获到 lambda 中。

通过复制或引用捕获 Lambda。因此,要捕获仅移动变量,您必须将其包装在复制 => 移动的对象中(例如std::auto_ptr)。这是一个令人讨厌的黑客攻击。

在您的示例中,您可以通过引用捕获,但如果这只是简化代码,它可能无法使用真实代码实现您想要的效果:

std::for_each(arr.begin(), arr.end(), [&p](int& i) { i+=*p; });

这是一个仅复制移动的包装器:

template<typename T>
struct move_on_copy_wrapper
{
    mutable T value;

    move_on_copy_wrapper(T&& t):
        value(std::move(t))
    {}

    move_on_copy_wrapper(move_on_copy_wrapper const& other):
        value(std::move(other.value))
    {}

    move_on_copy_wrapper(move_on_copy_wrapper&& other):
        value(std::move(other.value))
    {}

    move_on_copy_wrapper& operator=(move_on_copy_wrapper const& other)
    {
        value=std::move(other.value);
        return *this;
    }

    move_on_copy_wrapper& operator=(move_on_copy_wrapper&& other)
    {
        value=std::move(other.value);
        return *this;
    }

};

然后你可以像这样使用它:

int main()
{
    std::unique_ptr<int> p(new int(3));
    move_on_copy_wrapper<std::unique_ptr<int>> mp(std::move(p));

    [mp]()
    {
        std::cout<<"*mp.value="<<*mp.value<<std::endl;
    }
    ();

    std::cout<<"p="<<p.get()<<", mp="<<mp.value.get()<<std::endl;
}

【讨论】:

  • 你真的需要实现移动语义函数,即move_on_copy_wrapper(move_on_copy_wrapper&amp;&amp;)和move_on_copy_wrapper&amp; operator=(move_on_copy_wrapper&amp;&amp;)吗?编译器生成的还不够?
  • 由于拷贝构造函数和拷贝赋值运算符的存在,编译器不会生成它们。
  • "请注意,std::bind 还要求它的参数是可复制的。"这不是真的。 std::bind 可用于仅移动类型。
  • AFAIK,std::bind 应该适用于可移动参数。但是,MSDN 的文档说所有参数都应该是可复制构造的。 Anthony,您使用哪种编译器? msdn.microsoft.com/en-us/library/bb982702(v=vs.100).aspx
  • 好的,我检查了:std::bind 应该可以与只移动参数一起使用,它只会创建一个只移动结果。但是,它在 MSVC 2010 和 g++ 4.7 的实践中失败了。
【解决方案2】:

您的尝试 2 几乎可以成功。

缺少的是您没有告诉您的 bind 调用需要一个参数:

std::for_each(arr.begin(), arr.end(),
   std::bind([](const unique_ptr<int>& p, int& i){
      i += (*p);
   }, std::move(p), std::placeholders::_1)
);

placeholders::_1 是必要的,它可以告诉bind 的结果,它应该期待为它的operator() 传递一个参数。

这也是@marton78 的回答here 中给出的建议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-19
    • 2018-02-02
    • 1970-01-01
    • 2015-08-12
    • 1970-01-01
    • 2019-02-10
    相关资源
    最近更新 更多