从具有按值捕获的 lambda 移动构造 std::function 时调用两次移动构造函数

aaf*_*lei 14 c++ lambda move-semantics std-function c++17

当移动-构造std::function从一个对象的λ,其中该拉姆达具有由值捕获,看来该物体的移动,构造函数,是值捕获被调用两次。考虑

#include <功能>
#include <iostream>

结构体
{
    整数值 = 1;

    Foo() = 默认值;

    Foo(const Foo &) {}

    富(富&&)
    {
        std::cout << "移动构造函数" << std::endl;
    }
};

int main()
{
    福福;
    自动 lambda = [=]() { 返回 foo.value; };
    std::cout << "---------" << std::endl;
    std::function<int()> func(std::move(lambda));
    std::cout << "---------" << std::endl;
    返回0;
}

输出是

---------
move ctor
move ctor
---------
Run Code Online (Sandbox Code Playgroud)

我在 Mac OS X Catalina 上工作,我的编译器是

g++-9 (Homebrew GCC 9.3.0) 9.3.0
Run Code Online (Sandbox Code Playgroud)

我用g++ -std=c++17.

我想这种行为可能有点依赖于编译器实现,但我仍然对这种机制感到好奇。

有人可以解释为什么移动构造函数被调用两次以及那里到底发生了什么?

Dan*_*ica 9

这是由如何std::function实施引起的。考虑以下更简单的示例:

struct Lambda
{
  Lambda() = default;
  Lambda(const Lambda&) { std::cout << "C"; }
  Lambda(Lambda&&) { std::cout << "M"; }
  void operator()() const { }
};

int main()
{
  auto lambda = Lambda();
  std::function<void()> func(std::move(lambda));    
}
Run Code Online (Sandbox Code Playgroud)

它打印出MM,因此,Lambda在将其实例存储到 时,移动构造函数被调用了两次std::function

现场演示:https : //godbolt.org/z/XihNdC

在您的情况下,该Foolambda的成员变量(由值捕获)移动了两次,因为整个 lambda 移动了两次。请注意,捕获本身不会调用任何移动构造函数,而是调用复制构造函数。


为什么构造函数std::function将参数移动了两次?请注意,此构造函数按值传递其参数,然后在内部需要存储该对象。它可以通过以下函数进行模拟:

template< class F >
void function( F f )
{
    F* ptr = new F(std::move(f));
    delete ptr;
}
Run Code Online (Sandbox Code Playgroud)

这段代码:

  auto lambda = Lambda();
  function(std::move(lambda));
Run Code Online (Sandbox Code Playgroud)

然后执行两个动作

现场演示:https : //godbolt.org/z/qZvVWA

  • @aafulei,它没有调用 `function( function&amp;&amp; other );`。我们没有调用“移动构造函数”,因为我们不是从 *function* 对象本身进行构造。该构造来自可调用的 *F*,即“void function( F f )”(如解释中所写)。 (2认同)