#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型的。怎样实现呢?
巴扎黑2017-04-17 15:35:18
有以下方案:
用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;
}