是否可以避免将参数复制到 lambda 函数?
Is it possible to avoid copying arguments to a lambda function?
我想使用 Handle 管理文件描述符,我想使用 lambda 表达式来处理它们。我想使用 RAII 来管理底层文件描述符。一种选择是处理描述符的无效值(例如 -1(。但是,我希望句柄始终有效。
我发现我似乎无法避免至少调用一次复制构造函数。这是一个工作示例:
#include <fcntl.h>
#include <unistd.h>
#include <functional>
#include <system_error>
#include <iostream>
class Handle
{
public:
Handle(int descriptor) : _descriptor(descriptor) {}
~Handle()
{
std::cerr << "close(" << _descriptor << ")" << std::endl;
::close(_descriptor);
}
Handle(const Handle & other) : _descriptor(::dup(other._descriptor))
{
std::cerr << "dup(" << other._descriptor << ") = " << _descriptor << std::endl;
if (_descriptor == -1) throw std::system_error(errno, std::generic_category(), "dup");
}
int descriptor() const { return _descriptor; }
private:
int _descriptor;
};
Handle open_path(const char * path)
{
return ::open("/dev/random", O_RDONLY);
}
void invoke(std::function<void()> & function)
{
function();
}
int main(int argc, const char * argv[]) {
// Using auto f = here avoids the copy, but that's not helpful when you need a function to pass to another function.
std::function<void()> function = [handle = open_path("/dev/random")]{
std::cerr << "Opened path with descriptor: " << handle.descriptor() << std::endl;
};
invoke(function);
}
该程序的输出是:
dup(3) = 4
close(3)
Opened path with descriptor: 4
close(4)
我知道句柄正在被复制,因为它是在std::function
中按值分配的,但我的印象是在某些情况下可以分配堆std::function
这可能会避免复制(我想这不会发生(。
有许多选项,例如堆分配,或使用选中的哨兵值(例如-1(。但是,我希望有一个句柄始终有效的不变性。这有点像风格和不变性的问题。
有没有办法在std::function
的堆栈帧中构造句柄以避免复制,还是需要采取不同的方法?
也许作为补充一点:我们可以在多大程度上依赖std::function
来避免在创建时复制它的参数?
首先,让我们解决这个问题:std::function
与 lambda 完全正交。我写了一篇文章,"将函数传递给函数">,应该阐明它们之间的关系,并说明可用于在现代C++中实现高阶函数的各种技术。
在此处使用
auto f =
可以避免复制,但当您需要将一个函数传递给另一个函数时,这没有帮助。
我不同意。您可以在invoke
中使用模板或类似function_view
的东西(请参阅 LLVM 的FunctionRef
生产就绪实现,或我的文章了解另一个简单实现(:
template <typename F>
void invoke(F&& function)
{
std::forward<F>(function)();
}
void invoke(function_view<void()> function)
{
function();
}
依靠省略或随std::function
移动是不够的。由于std::function
要求是可复制的,因此您总是有可能在其他地方意外复制Handle
。
相反,您需要做的是将Handle
包装在不会在复制时调用复制构造函数的东西中。一个明显的选择是指针。一个明显的指针选择是像std::shared_ptr
这样的管理指针。
我对用于测试的Handle
类进行了一些更改(dtor,ctor,copy ctor 的打印语句(,因此我将首先展示这些更改:
class Handle
{
public:
Handle(int descriptor) : _descriptor(descriptor) {std::cerr<<"Default ctor, descriptor: " << _descriptor << std::endl;}
~Handle()
{
std::cerr << "Dtor. close(" << _descriptor << ")" << std::endl;
}
Handle(const Handle & other) : _descriptor(other._descriptor+1)
{
std::cerr << "Copy ctor. dup(" << other._descriptor << ") = " << _descriptor << std::endl;
}
int descriptor() const { return _descriptor; }
private:
int _descriptor;
};
接下来,让我们修改open_path
以返回一个shared_ptr
:
std::shared_ptr<Handle> open_path(const char * path)
{
return std::make_shared<Handle>(0);
}
然后我们将在main
中对我们的 lambda 进行轻微修改:
std::function<void()> function = [handle = open_path("/dev/random")]{
std::cerr << "Opened path with descriptor: " << handle->descriptor() << std::endl;
};
我们的输出现在变为:
Default ctor, descriptor: 0
Opened path with descriptor: 0
Dtor. close(0)
现场演示
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 我可以将调用类的"this"传递给 lambda 函数吗?
- 两组使用lambda函数的大括号
- 尝试将lambda函数放在队列中时出现一般分配器错误(可能是与unique_ptr有关的错误)
- 我可以在这里替换什么,因为我不能在 C# 中使用隐式变量的 lambda 函数?
- 为什么我不能在 constexpr lambda 函数中使用 std::tuple
- C++:Lambda 函数指针转换的用例是什么?
- 将 lambda 函数作为参数传递C++
- 如何将 lambda 函数作为参数发送到另一个函数
- 传递 lambda 函数的权衡是什么?
- 如何使用类模拟 C++11 中的 lambda 函数和闭包?
- 不是 lambda 函数中的常量表达式
- 如何使用可变参数数重载 lambda 函数?
- 如何通过指针传递lambda函数?
- 使用带有 lambda 函数指针的模板
- openmp c++ 中并行块内 lambda 函数的奇怪行为
- C++ 中 Lambda 函数中的溢出
- 将数组传递到 lambda 函数中
- lambda 函数未显示正确的结果