【发布时间】:2014-06-24 06:26:44
【问题描述】:
当我用捕获值的 lambda 构造 std::function 时,它会额外复制(移动)这些参数(实际上是我猜的整个 lambda 对象)。 代码:
#include <iostream>
#include <functional>
// Testing class - just to see constructing/destructing.
class T {
private:
static int idCounter; // The global counter of the constructed objects of this type.
public:
const int id; // Unique object ID
inline T() : id(++idCounter) {
std::cout << " Constuctor Id=" << id << std::endl;
};
inline T(const T& src) : id(++idCounter) {
std::cout << " Copy constructor Id=" << id << std::endl;
}
inline T(const T&& src) : id(++idCounter) {
std::cout << " Move constructor Id=" << id << std::endl;
}
inline void print() const {
std::cout << " Print is called for object with id=" << id << std::endl;
}
inline ~T() {
std::cout << " Destructor Id=" << id << std::endl;
}
};
int T::idCounter=0;
// Declare type of the std::function to store our lambda.
typedef std::function<int (void)> Callback;
int main()
{
std::cout << "Let's the game begin!" << std::endl;
T obj; // Custruct the first object.
std::cout << "Let's create a pointer to the lambda." << std::endl;
// Make a labmda with captured object. (The labmda prints and returns object's id).
// It should make one (local) copy of the captured object but it makes it twice - why?!
const Callback* pcb= new Callback( [obj]() -> int {
obj.print();
return obj.id;
} );
std::cout << "Now let's print lambda execution result." << std::endl;
std::cout << "The functor's id is " << (*pcb)() << std::endl;
std::cout << "Destroying the lambda." << std::endl;
delete pcb;
std::cout << "Terminating." << std::endl;
return 0;
}
输出是:
让我们的游戏开始! 构造函数 ID=1 让我们创建一个指向 lambda 的指针。 复制构造函数 ID=2 移动构造函数 Id=3 析构函数 ID=2 现在让我们打印 lambda 执行结果。 为 id=3 的对象调用打印 函子的 id 是 3 销毁 lambda。 析构函数 ID=3 终止。 析构函数 ID=1
我用捕获的对象制作了一个带有 lambda 的 std:function。它应该为 lambda 制作对象的本地副本,但它会复制两次(查看移动构造函数调用 - 以粗体突出显示)。实际上它会复制整个 lambda 对象。为什么?我怎样才能避免这种情况?
我正在使用 lambdas 进行线程间事件处理,它们可能会捕获大量的日期,所以我试图找到一种方法来避免不必要的复制。所以任务很简单——以最少的费用将构造的 lambda 传递给函数——如果它会为每个构造的 lambda 复制两次数据,我会寻找另一种处理事件的方法。
我正在使用强制 GNU C++11 的 GCC v4.7.2。
【问题讨论】:
-
移动
std::function的构造函数的初始化列表中的 lambda 时完成移动。这个 moving-the-lambda 强制捕获的对象也移动(即递归移动!)> -
@op,移动不是复制(当然你可以这样实现,但你为什么要这样做呢?)。您的测试类的一个明智的实现是不增加 id,而是将移动(临时)对象的 id 带到新实例。
-
在复杂项目的现实生活中,您不能保证搬家是便宜的。您正在使用第三方库、多线程问题等。例如 - 使用 10k 字符串移动 sdt:vector 是否便宜?
-
@user3544995 是的。大约四个指针分配。