传递具有移动捕获功能的 lambda
Passing a lambda with moved capture to function
我最近遇到了一个很难找到的错误。我尝试将 lambda 传递给采用std::function
对象的函数。lambda 正在捕获不可复制的对象。
我想通了,显然在所有传递之间必须发生一些副本。我得出这个结果是因为我总是以error: use of deleted function
错误告终。
以下是产生此错误的代码:
void call_func(std::function<void()> func)
{
func();
}
int main()
{
std::fstream fs{"test.txt", std::fstream::out};
auto lam = [fs = std::move(fs)] { const_cast<std::fstream&>(fs).close(); };
call_func(lam);
return 0;
}
我通过在std::shared_ptr
对象中std::fstream
对象来解决此问题。这工作正常,但我认为可能有一种更性感的方法可以做到这一点。
我现在有两个问题:
- 为什么会出现此错误?
- 我的想法:我在
for
循环中生成许多fstream
对象和 lambda,对于每个fstream
都有一个 lambda 写入它。因此,对fstream
对象的访问仅由 lambda 完成。我想为一些回调逻辑执行此操作。有没有像我尝试过的 lambda 更漂亮的方法?
发生此错误是因为您的 lambda 具有不可复制的捕获,使 lambda 本身不可复制。std::function
要求包装的对象是可复制构造的。
如果您可以控制call_func
,请将其设为模板:
template<typename T>
void call_func(T&& func)
{
func();
}
int main()
{
std::fstream fs{"test.txt", std::fstream::out};
auto lam = [fs = std::move(fs)] { const_cast<std::fstream&>(fs).close(); };
call_func(lam);
}
以下是我对(2(中您的想法的看法。由于std::function
要求包装的对象是可复制构造的,我们可以制作自己的函数包装器,它没有这个限制:
#include <algorithm>
#include <fstream>
#include <iterator>
#include <utility>
#include <memory>
#include <sstream>
#include <vector>
template<typename T>
void call_func(T&& func) {
func();
}
// All functors have a common base, so we will be able to store them in a single container.
struct baseFunctor {
virtual void operator()()=0;
};
// The actual functor is as simple as it gets.
template<typename T>
class functor : public baseFunctor {
T f;
public:
template<typename U>
functor(U&& f)
: f(std::forward<U>(f))
{}
void operator()() override {
f();
}
};
// In C++17 you don't need this: functor's default constructor can already infer T.
template<typename T>
auto makeNewFunctor(T&& v) {
return std::unique_ptr<baseFunctor>(new functor<T>{std::forward<T>(v)});
}
int main() {
// We need to store pointers instead of values, for the virtual function mechanism to behave correctly.
std::vector<std::unique_ptr<baseFunctor>> functors;
// Generate 10 functors writing to 10 different file streams
std::generate_n(std::back_inserter(functors), 10, [](){
static int i=0;
std::ostringstream oss{"test"};
oss << ++i << ".txt";
std::fstream fs{oss.str(), std::fstream::out};
return makeNewFunctor([fs = std::move(fs)] () mutable { fs.close(); });
});
// Execute the functors
for (auto& functor : functors) {
call_func(*functor);
}
}
请注意,虚拟调用的开销是不可避免的:由于您需要在同一容器中存储具有不同行为的函子,因此您基本上需要以一种或另一种方式进行多态行为。因此,您要么手动实现此多态性,要么使用virtual
.我更喜欢后者。
相关文章:
- "lambda capture"正常功能?
- 传递具有移动捕获功能的 lambda
- Lambda的功能C++奇怪的行为
- 将带捕获功能的 lambda 传递给模板化函数
- 基于lambda arity的专门功能模板
- 使用lambda在功能指针铸造时双免费
- 如何将功能机体转换为lambda
- 如何在C 中调用此lambda功能
- 如何将通用lambda传递到功能中
- 为什么在传递lambda而不是功能指针时不能推断模板参数
- 如何使用C lambda将成员功能指针转换为普通功能指针,以用作回调
- 如何将lambda功能传递给通用参数作为参数
- 未评估上下文中的默认模板参数和 lambda:错误还是功能?
- 为什么我的按钮类项目共享相同的lambda功能
- 用C 中的lambda替换功能
- Lambda通过参考传递的功能
- 如何否定lambda功能结果
- 如何更新嵌套lambda功能(C )中的变量
- C lambda作为班级成员的功能:奇怪的行为
- 尝试在静态功能(C )中使用lambda函数