Alternative to std::function for passing function as argument (callbacks, etc.)

前端 未结 3 1902
面向向阳花
面向向阳花 2020-12-29 06:14

I stumbled across this during my experiments with C++11. I find that it is an obvious solution, but I haven\'t been able to find any other examples of it in the wild, so I\'

3条回答
  •  無奈伤痛
    2020-12-29 07:14

    auto callback(std::future) -> void is the declaration of a entity of type void(std::future) called callback. When listed as an argument, the compiler adjusts this to be a pointer-to-function of type void(*)(std::future).

    Your lambda is stateless, and as such can be implicitly converted to a function pointer.

    Once you add a non-trivial capture, your code will stop compiling:

    [argc](std::future number) { 
       std::cout << argc << '\n';
    

    ...

    Now, ignoring your question content and looking at the title...

    There is a modest cost for a std::function because it is a value-type, not a view-type. As a value-type, it actually copies its argument.

    You can get around this by wrapping the calling object in a std::ref, but if you want to state "I won't keep this function object around longer than this call", you can write a function_view type as follows:

    template
    struct function_view;
    
    template
    struct function_view {
      void* ptr = nullptr;
      R(*pf)(void*, Args...) = nullptr;
    
      template
      using pF = decltype(std::addressof( std::declval() ));
    
      template
      void bind_to( F& f ) {
        ptr = (void*)std::addressof(f);
        pf = [](void* ptr, Args... args)->R{
          return (*(pF)ptr)(std::forward(args)...);
        };
      }
      // when binding to a function pointer
      // even a not identical one, check for
      // null.  In addition, we can remove a
      // layer of indirection and store the function
      // pointer directly in the `void*`.
      template
      void bind_to( R_in(*f)(Args_in...) ) {
        using F = decltype(f);
        if (!f) return bind_to(nullptr);
        ptr = (void*)f;
        pf = [](void* ptr, Args... args)->R{
          return (F(ptr))(std::forward(args)...);
        };
      }
      // binding to nothing:
      void bind_to( std::nullptr_t ) {
        ptr = nullptr;
        pf = nullptr;
      }       
      explicit operator bool()const{return pf;}
    
      function_view()=default;
      function_view(function_view const&)=default;
      function_view& operator=(function_view const&)=default;
    
      template>{}, int > =0,
        std::enable_if_t< std::is_convertible< std::result_of_t< F&(Args...) >, R >{}, int> = 0
      >
      function_view( F&& f ) {
        bind_to(f); // not forward
      }
    
      function_view( std::nullptr_t ) {}
    
      R operator()(Args...args) const {
          return pf(ptr, std::forward(args)...);
      }
    };
    

    live example.

    This is also useful in that it is a strictly simpler kind of type erasure than std::function, so it could be educational to go over it.

提交回复
热议问题