C++ 将带有移动捕获的lambda传递到函数
我最近遇到了一个很难找到的bug。我试图将lambda传递给接受C++ 将带有移动捕获的lambda传递到函数,c++,lambda,c++14,C++,Lambda,C++14,我最近遇到了一个很难找到的bug。我试图将lambda传递给接受std::function对象的函数。lambda正在捕获一个不可复制的对象 我想,很明显,在所有的过路之间一定会有一些复制品。我得出这个结果是因为我总是以一个错误结束:使用deleted function错误 以下是产生此错误的代码: void call_func(std::function<void()> func) { func(); } int main() { std::fstream fs{
std::function
对象的函数。lambda正在捕获一个不可复制的对象
我想,很明显,在所有的过路之间一定会有一些复制品。我得出这个结果是因为我总是以一个错误结束:使用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;
}
void调用函数(std::function func)
{
func();
}
int main()
{
std::fstream fs{“test.txt”,std::fstream::out};
自动lam=[fs=std::move(fs)]{const_cast(fs).close();};
呼叫_func(林);
返回0;
}
我通过在std::shared_ptr
对象中设置std::fstream
对象的上限来解决这个问题。这很好,但我认为可能有一个更性感的方式来做到这一点
我现在有两个问题:
for
循环中生成许多fstream
对象和lambda,对于每个fstream
都有一个lambda写入。因此,对fstream
对象的访问仅由lambda完成。我想这样做是为了一些回调逻辑。有没有像我试过的那样,用lambdas做这件事更漂亮的方法发生此错误是因为lambda具有不可复制的捕获,从而使lambda本身不可复制<代码>标准::函数 如果您可以控制调用函数,请将其作为模板:
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);
}
请注意,虚拟调用的开销是不可避免的:因为您需要在同一容器中存储具有不同行为的functor,所以您本质上需要某种多态行为。因此,您可以手动实现此多态性,也可以使用
virtual
。我更喜欢后者。/OT:不要使用常量投射
,而是将lambda标记为可变
。顺便说一句,您的常量投射
是UB。不能修改以这种方式浇铸的对象。正如Rakete1111指出的那样,正确的方法是标记对象mutable
为什么我必须使其可变?@CássioRenan这里是什么?成员变量被捕获为非常量;只有生成的运算符()
被标记为const
。将const
从const
左值转换为非const
对象以修改所述对象是完全正确的,const\u转换的整个点
。当然,我不认为这是一种不好的风格,但它不是UB。或者如果是,为什么?@Julian,因为这就是如何获得一个操作符()
,它不是const
-限定的,因此提供对lambda的捕获成员的读写访问权。如果知道它只运行一次,那么可变的lambda没有错。例如,将可变lambda推送到工作队列是没有风险的;呼叫_func(林);对我来说也会有同样的错误。您的其他解决方案不适合,因为调用lambda时std::fstream对象很长时间都超出范围。@Julian对不起,我不理解您的问题。您可以将一个可复制对象移动到lambda中,并且可以将其与std::function
一起正常使用。e、 g:仅仅因为你在移动对象,这并不意味着对象必须是不可复制的。我在第(2)条中编辑了我对你想法的看法,强调你是对的。编辑的答案,以消除不必要的变化,在OP的代码。@下划线,老实说,我不认为这两个是好的风格。但那只是我。有太多的方法可以让细微的改变引入无声的bug。在回应上面弗朗索瓦的评论时,我引用了其中一种方式。另外,const_cast
还有一个问题,即如果您确实向lambda传递了一个const对象,它将导致UB而不是编译器错误(正如mutable
lambda所做的那样)。
#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);
}
}