Ошибка возникает из-за того, что ваша лямбда имеет не копируемые захваты, что делает саму лямбду не копируемой. 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
. Я предпочитаю последнее.