60

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 types ArgTypes and return type R. 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?

Community
  • 1
  • 1
orm
  • 2,445
  • 1
  • 17
  • 33
  • 8
    `std::function` is broken in several different ways... I think that's generally accepted, but very difficult to fix without breaking existing code. – Kerrek SB Aug 15 '14 at 17:06
  • 11
    Hey. Thanks for the comment. Now that you mention it, it would be nice to hear some specific ways in which it is broken. – orm Aug 15 '14 at 17:09
  • possible duplicate of [Why the initializer of std::function has to be CopyConstructible?](http://stackoverflow.com/questions/24658546/why-the-initializer-of-stdfunction-has-to-be-copyconstructible) – Nevin Aug 15 '14 at 17:57
  • @Nevin. that question overlaps, but I am also asking whether the std library has or maybe plans to offer a non-copyable version of std::function that has a deleted copy constructor. It does explain partly why you cannot have std::function work in both cases, but not why there aren't two versions of std::function. The technical problem described in that answer does not address the fact that we don't need to declare a copy constructor. – orm Aug 15 '14 at 19:00
  • 5
    @KerrekSB I don't think *that* particular aspect is broken. Since `function` performs type erasure, it would become a run-time problem whether or not that *instance* of `function` is copyable. – dyp Aug 15 '14 at 19:34
  • The LEWG (Library Evolution Working Group of the C++ Committee) has had some internal discussions on it, but no papers have yet emerged. – Nevin Aug 15 '14 at 19:40
  • 3
    Well you can [reuse `std::function` to make it work](http://coliru.stacked-crooked.com/a/340d6aa74b289b9c).. kind of (note that `function_mo` itself is move-only, so no exceptions of `hack` will be thrown). – dyp Aug 15 '14 at 19:47
  • 4
    @orm: One of the big sticking points is that the function call operator is `const`, which the library requires to mean thread-safe. This makes it hard for people who want to use `function` as a generic callable thing in concurrent settings. Another aspect that's somewhat half-baked is the type-erased allocator support, I believe (esp. regarding fancy pointers); `function` is the only class in the library that has a type-erased allocator and is also copyable. (See N3916 for some aspects. [N4041](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4041.html) is also interesting.) – Kerrek SB Aug 15 '14 at 23:02
  • @KerrekSB Doesn't the const only means that the function call operator is thread safe for the `std::function` instance not for the underlying type erased callable ? – Drax Aug 20 '14 at 16:04
  • @Drax, the underlying type erased object is also member of the std::function. So, any underlying methods accessed by the external call operator need to be const as well. – orm Aug 20 '14 at 19:06
  • @orm not sure about that, if that member is a pointer-like, it is the pointer that is const (you cannot modify the thing that allows the indirection) not the pointed object (you can modify the thing that your pointer allows you to access). – Drax Aug 21 '14 at 08:19
  • Ah, @Drax, you're right. I also don't quite follow how the const part makes it hard to use in concurrent settings. If anything, it sounds like it should make it easier to use. – orm Aug 21 '14 at 09:09
  • 1
    @dyp FYI your function_mo example blows up when initialized with a lambda: http://coliru.stacked-crooked.com/a/d9eea20f67d6e578 (or any non-DefaultConstructible type) – Innocent Bystander Sep 17 '18 at 03:21
  • 2
    There is a [proposal for `std::any_invocable`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0288r4.html) (sometimes called `unique_function`) that has been approved but won’t make C++20. – Davis Herring Aug 22 '19 at 12:52

2 Answers2

18

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).

Community
  • 1
  • 1
Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
  • 2
    Your suggestion to use the "impossibly fast delegates" code is a red herring: the essential trick it uses to speed things up is specific to method pointers (and from the comments it looks like it doesn't actually speed things up anyway). As written, it won't work on user-defined lambdas (or other functors), which is what the original question was asking about. Your example code seems to be more like a conventional `std::function` implementation. – Arthur Tacca Jun 14 '18 at 08:41
  • @arthur The delegates code can be easily modified to consume `operator()` implicitly (ignoring template cases) in my experience. I do not know if it still gives a performance boost; it has been a long time since I used it in other than legacy situations (it is too much of a pain to maintain). – Yakk - Adam Nevraumont Jun 14 '18 at 11:20
  • What do you mean by "consume `operator()`"? Do you mean consume arbitrary classes that have an `operator()` (including lambda functions)? If so, that is most of the effort of writing a type-erased functor. Whether you consider it difficult or easy, there's no much point linking to something that doesn't do it (especially something that spends extra effort doing something unrelated). – Arthur Tacca Jun 14 '18 at 11:34
  • @arthur it is an example of zero allocation std function like; mine is more complete but allocates. – Yakk - Adam Nevraumont Jun 14 '18 at 12:16
9

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;
}

Working version to coliru.

Innocent Bystander
  • 4,998
  • 18
  • 37
  • public base allows conversion to const std :: function & and copying (with exception) – pal Nov 04 '18 at 19:32
  • @pal Converting to `std::function` is so weird (?). In that way we can afterwards copy `std::function ff = f3; auto fff = ff;` How is that possible? – Gabriel Apr 22 '20 at 19:53
  • This implementation is somewhat flawed: `unique_function(Fn&& f) ` matches to much I think... `fn a; fn b; a = b;` compiles but should not... – Gabriel Apr 22 '20 at 20:03
  • I simply disabled that template for `unique_function` itself. – WolleTD Nov 15 '20 at 15:36
  • Should remove the possibility of storing a reference to the callable `unique_function(Fn&& f) : base(wrapper>{ std::forward>(f) }) { }` and same for `operator=` – Ricky Lung Mar 04 '21 at 03:39
  • @RickyLung feel free to edit the answer. It's been a few years and it will take me a while to reorient myself to it. – Innocent Bystander Mar 04 '21 at 22:20