问题描述
#include <memory>#include <functional>class A{ //non-copyable std::unique_ptr<int> a;public: void operator()(){} //non-const};void func(std::function<void(void)> f){}int main(){ A fobj; func(fobj); return 0;}
如上,需要传递一个A的函数对象给func,并且fobj不能是const型的。怎样实现呢?
问题解答
回答1:有以下方案:
用A的引用。缺点:A被销毁时引用随之失效。
通过移动语义构造一个shared_ptr。缺点:额外的运行时开销,需要A可移动构造。
#include <memory>#include <functional>#include <utility>class A{ //non-copyable std::unique_ptr<int> a;public: void operator()(){} //non-const};void func(std::function<void(void)> f) {}int main(){ A obj; // by reference func([&obj](){obj();}); // by shared_ptr auto ptr = std::make_shared<A>(std::move(obj)); func([ptr](){(*ptr)();}); return 0;}
或者通过一个在拷贝时移动对象的wrapper来实现(需要A可移动构造):
#include <memory>#include <functional>#include <type_traits>class A{ //non-copyable std::unique_ptr<int> a;public: void operator()(){} //non-const};void func(std::function<void(void)> f) {}template <class Fn, class Ret, class... Args>struct FunctionWrapperImpl { FunctionWrapperImpl(FunctionWrapperImpl &wrapper) : fn_(std::move(wrapper.fn_)) {} FunctionWrapperImpl(FunctionWrapperImpl &&wrapper) : fn_(std::move(wrapper.fn_)) {} FunctionWrapperImpl(Fn &&fn) : fn_(std::move(fn)) {} template <class T = Ret> std::enable_if_t<std::is_same<void, T>::value, Ret> operator()(Args... args) { fn_(std::forward<Args>(args)...); } template <class T = Ret> std::enable_if_t<!std::is_same<void, T>::value, Ret> operator()(Args... args) { return fn_(std::forward<Args>(args)...); } Fn fn_;};template <class Fn>struct FunctionWrapper { FunctionWrapper(FunctionWrapper &&wrapper) : fn_(std::move(wrapper.fn_)) {} FunctionWrapper(Fn &&fn) : fn_(std::move(fn)) {} template <class Ret, class... Args> operator std::function<Ret(Args...)>() && { return std::function<Ret(Args...)>(FunctionWrapperImpl<Fn, Ret, Args...>(std::move(fn_))); } Fn fn_;};template <class Fn>auto function_wrapper(Fn &&fn) { return FunctionWrapper<Fn>(std::move(fn));}int main(){ A obj; // function wrapper func(function_wrapper(std::move(obj))); return 0;}