C++ 转让所有权时按价值捕获Lambda

C++ 转让所有权时按价值捕获Lambda,c++,c++11,c++14,C++,C++11,C++14,我有一个RAII风格的类,它管理特定资源的所有权。因此,复制构造函数和赋值运算符被显式删除,只存在移动变量,它们移动资源并使源(引用)无效。到目前为止,它工作得很好,但现在我想将这种对象移动到其他地方处理的std::函数中,以便该对象成为所有权载体,并最终释放该对象。幸运的是,C++14允许用户在按值捕获时使用移动赋值,所以看起来这是一条可行之路。比如: std::function act=[temp=move(resCarrier),description]()可变{ 试一试{ //希望例外安

我有一个RAII风格的类,它管理特定资源的所有权。因此,复制构造函数和赋值运算符被显式删除,只存在移动变量,它们移动资源并使源(引用)无效。到目前为止,它工作得很好,但现在我想将这种对象移动到其他地方处理的std::函数中,以便该对象成为所有权载体,并最终释放该对象。幸运的是,C++14允许用户在按值捕获时使用移动赋值,所以看起来这是一条可行之路。比如:

std::function act=[temp=move(resCarrier),description]()可变{
试一试{
//希望例外安全
自动c=使_唯一(移动(临时)、描述);
c->doStuff();
}捕获(…){}
};
调度程序->稍后运行(移动(动作));
但不是那么容易。它应该能工作,但不能。得到混乱的模板错误,最终以此结束(为清晰起见稍微缩短)

/usr/include/c++/10/bits/std_function.h:161:6:错误:使用已删除的函数“::&”
...
注意:“::&)”被隐式删除,因为默认定义的格式不正确:
99 | std::function act=[temp=move(resCarrier),description]()可变
|                                                                    ^
...
错误:使用已删除的函数“TResCarrier::TResCarrier(const TResCarrier&)”
因此,看起来编译器基本上忽略了移动语义,并坚持在捕获lambda声明中的变量时使用复制构造函数。到目前为止,我无法说服它以不同的方式处理。到目前为止,“最好”的想法是围绕所携带的对象创建另一个级别的重定向,以便可以通过值复制和捕获它(如围绕资源的共享ptr)。但这将是运行时开销,没有什么好的理由


是我忽略了什么,还是std::function背后隐藏着令人讨厌的类型擦除限制,还是lambda到函数映射的限制?

lambda不是
std::function

您是对的,c++14允许将某些内容移动到lambda中,甚至在之后移动lambda

另一方面,
std::function
要求可调用函数是可复制构造和可复制分配的

类模板std::function是一个通用多态函数包装器。函数的实例可以存储、复制和调用任何可复制的可调用目标函数、lambda表达式、绑定表达式或其他函数对象,以及指向成员函数的指针和指向数据成员的指针

存储的可调用对象称为std::function的目标。如果std::函数不包含目标,则称为空。调用空std::函数的目标会导致引发std::bad_function_调用异常

函数满足可复制可构造和可复制可分配的要求

可能的解决方案的一些想法可以是:

  • 创建自己的函数包装器(或使用库),该包装器支持仅移动可调用项

  • 将您的
    std::unique_pre
    包装在
    std::shared_ptr
    中,并捕获它

第一种选择似乎更为稳健,但仅仅是为了展示快速解决棘手问题的想法

#include <memory>
#include <functional>

int main() {
    auto moveonly = std::make_unique<int>(5);
    auto wrapped = std::make_shared<std::unique_ptr<int>>(std::move(moveonly));

    auto lam = [wrapped]() {
        return *(*wrapped);
    };

    std::function<int()> f = lam;
}
#包括
#包括
int main(){
auto moveonly=std::使_唯一(5);
自动包装=标准::使共享(标准::移动(仅移动));
自动林=[wrapped](){
退货*(*已包装);
};
std::函数f=lam;
}

lambda不是
std::function

您是对的,c++14允许将某些内容移动到lambda中,甚至在之后移动lambda

另一方面,
std::function
要求可调用函数是可复制构造和可复制分配的

类模板std::function是一个通用多态函数包装器。函数的实例可以存储、复制和调用任何可复制的可调用目标函数、lambda表达式、绑定表达式或其他函数对象,以及指向成员函数的指针和指向数据成员的指针

存储的可调用对象称为std::function的目标。如果std::函数不包含目标,则称为空。调用空std::函数的目标会导致引发std::bad_function_调用异常

函数满足可复制可构造和可复制可分配的要求

可能的解决方案的一些想法可以是:

  • 创建自己的函数包装器(或使用库),该包装器支持仅移动可调用项

  • 将您的
    std::unique_pre
    包装在
    std::shared_ptr
    中,并捕获它

第一种选择似乎更为稳健,但仅仅是为了展示快速解决棘手问题的想法

#include <memory>
#include <functional>

int main() {
    auto moveonly = std::make_unique<int>(5);
    auto wrapped = std::make_shared<std::unique_ptr<int>>(std::move(moveonly));

    auto lam = [wrapped]() {
        return *(*wrapped);
    };

    std::function<int()> f = lam;
}
#包括
#包括
int main(){
auto moveonly=std::使_唯一(5);
自动包装=标准::使共享(标准::移动(仅移动));
自动林=[wrapped](){
退货*(*已包装);
};
std::函数f=lam;
}

你能显示一个吗?你至少不缺少使用过的
DataProcessor::DataProcessor
scheduler->RunLater
。几乎没有,剩下的信息都在描述中。你能显示一个吗?你至少不缺少使用过的
DataProcessor::DataProcessor
scheduler->RunLater
。几乎没有,剩下的信息在描述中。我知道lambda!=std::function,但我不确定是否编写了隐式转换规则。感谢您指出了可复制的需求,遗憾的是,这意味着预期的灵活性不可能按预期实现。如前所述,我已经在考虑共享的ptr包装,但出于性能原因,我可能会采取另一种方式