'Move-only version of std::function
Because std::function
is copyable, the standard requires that callables used to construct it also be copyable:
n337 (20.8.11.2.1)
template<class F> function(F f);
Requires:
F
shall be CopyConstructible.f
shall be Callable (20.8.11.2) for argument typesArgTypes
and return typeR
. The copy constructor and destructor of A shall not throw exceptions.`
This implies that it is not possible to form an std::function
from a non-copyable bind object or a lambda that captured a move-only type such as std::unique_ptr
.
It seems possible to implement such a move-only wrapper for move-only callables. Is there a standard library move-only equivalent for std::function
or, is there a common workaround for this problem?
Solution 1:[1]
No, there is no move-only version of std::function
in the C++ std
library. (As of C++14)
Fastest possible delegates is an implementation of a std::function
like class that happens to be faster than most std::function
implementations in many std
libraries, and it should be easy to fork into a move
and copy
version.
Wrapping your move
only function object into a shared_ptr<F>
in a class with a forwarding operator()
is another approach.
Here is a task
sketch:
template<class Sig>
struct task;
namespace details {
template<class Sig>
struct task_iimpl;
template<class R, class...Args>
struct task_iimpl<R(Args...)> {
virtual ~task_iimpl() {}
virtual R invoke(Args&&...args) const = 0;
};
template<class F, class Sig>
struct task_impl;
template<class F, class R, class...Args>
struct task_impl<F,R(Args...)>:
task_iimpl<R(Args...)>
{
F f;
template<class T>
task_impl(T&& t):f(std::forward<T>(t)) {}
virtual R invoke(Args&&...args) const override {
return f( std::forward<Args>(args...) );
}
};
template<class F, class...Args>
struct task_impl<F,void(Args...)>:
task_iimpl<void(Args...)>
{
F f;
template<class T>
task_impl(T&& t):f(std::forward<T>(t)) {}
virtual void invoke(Args&&...args) const override {
f( std::forward<Args>(args...) );
}
};
}
template<class R, class...Args>
struct task<R(Args...)> {
virtual ~task_iimpl() {}
R operator()(Args...args) const {
return pImpl->invoke(std::forward<Args>(args...));
}
explicit operator bool()const{ return static_cast<bool>(pImpl); }
task(task &&)=default;
task& operator=(task &&)=default;
task()=default;
// and now for a mess of constructors
// the rule is that a task can be constructed from anything
// callable<R(Args...)>, destroyable, and can be constructed
// from whatever is passed in. The callable feature is tested for
// in addition, if constructed from something convertible to `bool`,
// then if that test fails we construct an empty task. This makes us work
// well with empty std::functions and function pointers and other tasks
// that are call-compatible, but not exactly the same:
struct from_func_t {};
template<class F,
class dF=std::decay_t<F>,
class=std::enable_if_t<!std::is_same<dF, task>{}>,
class FR=decltype(std::declval<F const&>()(std::declval<Args>()...)),
std::enable_if_t<std::is_same<R, void>{} || std::is_convertible<FR, R>{} >*=0,
std::enable_if_t<std::is_convertible<dF, bool>{}>*=0
>
task(F&& f):
task(
static_cast<bool>(f)?
task( from_func_t{}, std::forward<F>(f) ):
task()
)
{}
template<class F,
class dF=std::decay_t<F>,
class=std::enable_if_t<!std::is_same<dF, task>{}>,
class FR=decltype(std::declval<F const&>()(std::declval<Args>()...)),
std::enable_if_t<std::is_same<R, void>{} || std::is_convertible<FR, R>{} >*=0,
std::enable_if_t<!std::is_convertible<dF, bool>{}>*=0
>
task(F&& f):
task( from_func_t{}, std::forward<F>(f) )
{}
task(std::nullptr_t):task() {}
// overload resolution helper when signatures match exactly:
task( R(*pf)(Args...) ):
task( pf?task( from_func_t{}, pf ):task() )
{}
private:
template<class F,
class dF=std::decay_t<F>
>
task(from_func_t, F&& f):
pImpl( std::make_unique<details::task_impl<dF,R(Args...)>>(
std::forward<F>(f)
)
{}
std::unique_ptr<details::task_iimpl<R(Args...)> pImpl;
};
but it has not been tested or compiled, I just wrote it.
A more industrial strength version would include a small buffer optimization (SBO) to store small callables (assuming they are movable; if not movable, store on heap to allow moving), and a get-pointer-if-you-guess-the-type-right (like std::function
).
Solution 2:[2]
As others have pointed out, there is no move-only version of std::function
in the library. Following is a work-around that the reuses (abuses?) std::function
and allows it to accept move-only types. It is largely inspired by dyp's implementation in the comments, so a lot of the credit goes to him:
#include <functional>
#include <iostream>
#include <type_traits>
#include <utility>
template<typename T>
class unique_function : public std::function<T>
{
template<typename Fn, typename En = void>
struct wrapper;
// specialization for CopyConstructible Fn
template<typename Fn>
struct wrapper<Fn, std::enable_if_t< std::is_copy_constructible<Fn>::value >>
{
Fn fn;
template<typename... Args>
auto operator()(Args&&... args) { return fn(std::forward<Args>(args)...); }
};
// specialization for MoveConstructible-only Fn
template<typename Fn>
struct wrapper<Fn, std::enable_if_t< !std::is_copy_constructible<Fn>::value
&& std::is_move_constructible<Fn>::value >>
{
Fn fn;
wrapper(Fn&& fn) : fn(std::forward<Fn>(fn)) { }
wrapper(wrapper&&) = default;
wrapper& operator=(wrapper&&) = default;
// these two functions are instantiated by std::function
// and are never called
wrapper(const wrapper& rhs) : fn(const_cast<Fn&&>(rhs.fn)) { throw 0; } // hack to initialize fn for non-DefaultContructible types
wrapper& operator=(wrapper&) { throw 0; }
template<typename... Args>
auto operator()(Args&&... args) { return fn(std::forward<Args>(args)...); }
};
using base = std::function<T>;
public:
unique_function() noexcept = default;
unique_function(std::nullptr_t) noexcept : base(nullptr) { }
template<typename Fn>
unique_function(Fn&& f) : base(wrapper<Fn>{ std::forward<Fn>(f) }) { }
unique_function(unique_function&&) = default;
unique_function& operator=(unique_function&&) = default;
unique_function& operator=(std::nullptr_t) { base::operator=(nullptr); return *this; }
template<typename Fn>
unique_function& operator=(Fn&& f)
{ base::operator=(wrapper<Fn>{ std::forward<Fn>(f) }); return *this; }
using base::operator();
};
using std::cout; using std::endl;
struct move_only
{
move_only(std::size_t) { }
move_only(move_only&&) = default;
move_only& operator=(move_only&&) = default;
move_only(move_only const&) = delete;
move_only& operator=(move_only const&) = delete;
void operator()() { cout << "move_only" << endl; }
};
int main()
{
using fn = unique_function<void()>;
fn f0;
fn f1 { nullptr };
fn f2 { [](){ cout << "f2" << endl; } }; f2();
fn f3 { move_only(42) }; f3();
fn f4 { std::move(f2) }; f4();
f0 = std::move(f3); f0();
f0 = nullptr;
f2 = [](){ cout << "new f2" << endl; }; f2();
f3 = move_only(69); f3();
return 0;
}
Solution 3:[3]
Yes, there is a proposal for std::move_only_function in the current draft of C++23, adopted 2021-10:
This paper proposes a conservative, move-only equivalent of
std::function
.
See also the cppreference entry on std::move_only_function:
Class template std::move_only_function is a general-purpose polymorphic function wrapper. std::move_only_function objects can store and invoke any constructible (not required to be move constructible) Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to member objects.
...
std::move_only_function satisfies the requirements of MoveConstructible and MoveAssignable, but does not satisfy CopyConstructible or CopyAssignable.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Community |
Solution 2 | |
Solution 3 |