c++ - 替代 std::function 将函数作为参数传递(回调等)

标签 c++ c++11

我在使用 C++11 进行实验时偶然发现了这一点。我发现这是一个明显的解决方案,但我无法在野外找到任何其他示例,所以我担心我遗漏了一些东西。

我指的做法(在“addAsync”函数中):

#include <thread>
#include <future>
#include <iostream>
#include <chrono>

int addTwoNumbers(int a, int b) {
    std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;

    return a + b;
}

void printNum(std::future<int> future) {
    std::cout << future.get() << std::endl;
}

void addAsync(int a, int b, auto callback(std::future<int>) -> void) { //<- the notation in question
    auto res = std::async(std::launch::async, addTwoNumbers, a, b);

    if (callback) //super straightforward nullptr handling
        return callback(std::move(res));
}

int main(int argc, char** argv) {
    addAsync(10, 10, [](std::future<int> number) { //lambda functions work great
        addAsync(number.get(), 20, [](std::future<int> number) {
            addAsync(893, 4387, printNum); //as do standard functions
            addAsync(2342, 342, nullptr); //executes, sans callback

            std::cout << number.get() << std::endl;
        });
    });

    std::cout << "main thread: " << std::this_thread::get_id() << std::endl;

    return 0;
}

这被认为是不好的做法,还是不可移植(我只在 MSVC++ 2015 中尝试过)?另外,编译器如何处理这个;通过转换为 std::function?

我很想在我的项目中继续使用它,因为它显然在“签名”中声明了所需的参数类型和返回类型,接受 nullptr 作为可选性,并且似乎“正常工作”(我知道这些是 C++ 中著名的遗言)。

最佳答案

auto callback(std::future<int>) -> voidvoid(std::future<int>) 类型实体的声明调用callback .当作为参数列出时,编译器将其调整为 void(*)(std::future<int>) 类型的函数指针。 .

您的 lambda 是无状态的,因此可以隐式转换为函数指针。

一旦您添加了重要的捕获,您的代码将停止编译:

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

...

现在,忽略您的问题内容并查看标题...

std::function 的成本适中因为它是值类型,而不是 View 类型。作为一个值类型,它实际上复制了它的参数。

您可以通过将调用对象包装在 std::ref 中来解决此问题。 ,但是如果你想声明“我不会让这个函数对象比这个调用更久”,你可以写一个 function_view输入如下:

template<class Sig>
struct function_view;

template<class R, class...Args>
struct function_view<R(Args...)> {
  void* ptr = nullptr;
  R(*pf)(void*, Args...) = nullptr;

  template<class F>
  using pF = decltype(std::addressof( std::declval<F&>() ));

  template<class F>
  void bind_to( F& f ) {
    ptr = (void*)std::addressof(f);
    pf = [](void* ptr, Args... args)->R{
      return (*(pF<F>)ptr)(std::forward<Args>(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<class R_in, class...Args_in>
  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>(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<class F,
    std::enable_if_t< !std::is_same<function_view, std::decay_t<F>>{}, 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>(args)...);
  }
};

live example .

这也很有用,因为它是一种比 std::function 更简单的类型删除。 ,所以复习一下可能很有教育意义。

关于c++ - 替代 std::function 将函数作为参数传递(回调等),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39087141/

相关文章:

c++ - 如何将数据传递给不同的线程

c++ - 在 C++ 的 cmd 中显示 Pascal 的三角形

c++ - 带空的算术运算?

c++ - 谁能告诉我我的代码有什么问题?

c++ - 使用 ndk-build 链接现有的静态库

c++ - Boost::Multiindex 与字符串索引 boost::unordered_map

c++ - 析构函数立即调用并删除我的数组

c++ - 用于查看互联网连接和构建防火墙的 Shell 脚本或 C++ 库

c++ - c++ 11中结构数组的大括号初始化

c++ - move 语义说明