首页 > 其他分享 >[how does it work series] std::bind

[how does it work series] std::bind

时间:2023-12-25 22:46:32浏览次数:26  
标签:std ... args seq int bind work

本文不是一篇对std::bind的源码分析,而是试图通过逐步推导的方式,不断迭代优化,最终实现一版能阐述清核心原理的demo。非常像真实的开发过程。

事实上,关于std::bind的源码分析已有优质的讲解,建议想深入了解的读者参阅。

什么是std::bind?

std::bind 是 C++ 标准库中的一个函数模板,它用于创建一个可调用对象(callable object)。通过 std::bind,可以将一个函数与其参数绑定为一个可调用对象,从而延迟函数的调用或者改变函数的调用方式。下面是个例子:

#include <iostream>
#include <functional>

void printSum(int a, int b) {
    std::cout << "Sum: " << a + b << std::endl;
}

int main() {
    auto bindFunc = std::bind(printSum, 10, 20);
    bindFunc(); // 调用绑定的函数
    return 0;
}

实现

明确目的

bind函数接受一个callable实例(可能是函数指针、lambda或functor等),以及一系列不定参数;返回一个有着新签名(signature)的callable实例。据此我们可以给出第一版设计(注意并不能编译):

template<typename TFunc, typename ...TArgs>
struct bind_result {
  explicit bind_result(TFunc func, TArgs ...args)
    : func(std::move(func)), args(args...)
  {}

  template<typename ...TRealArgs>
  void operator()(TRealArgs ...real_args) {
    func(args.../* not compile */, real_args...);
  }

 private:
  TFunc func;
  std::tuple<TArgs...> args;
};

问题在于我们无法直接申明模板函数包(template argument pack)类型的成员变量,只能用std::tuple包一层;而std::tuple无法使用折叠表达式(fold expression)语法来展开。这样导致注释处出现编译错误。我们需要解决展开std::tuple的问题。

展开tuple

恰巧笔者在SO上读到这篇文章,里面提供了将tuple展开的思路,先给出实现,再进行解释。

template<int ...i>
struct seq {};

template<int i, int ...pack>
struct gen_seq {
  using type = typename gen_seq<i - 1, i - 1, pack...>::type;
};

template<int ...pack>
struct gen_seq<0, pack...> {
  using type = seq<pack...>;
};

下面对该trait举例说明。考虑gen_seq<3>这个例子,其模板参数列表(template<int i, int ...pack>)中的i等于3,pack为空,那么using type = typename gen_seq<i - 1, i - 1, pack...>::type会被解释为using type = gen_seq<2, 2>::type。以此类推,gen_seq<2, 2>::type会被最终解释为seq<0, 1, 2>

以下例子展示了展开tuple的全流程,理解此例子有助于理解后面的代码:

template<int ...i>
void print_i(seq<i...>) {
  /* fold expression in C++17 */
  (printf("%d ", i), ...);
}

int main() {
  print_i(gen_seq<3>::type{});   // (1)
}

解释:标注(1)处利用gen_seq创建seq<0,1,2>实例,该实例变量又指导编译器推理出模板参数,最终利用折叠表达式(fold expression)逐个将0,1,2在console中输出。

阶段性成果

更新bind_result,解决编译错误,我们可以得到一个能work的阶段性结果。值得一提的是,我们的bind尚且存在许多局限性,还无法应用于生产环境,后面的文章会继续优化。个人觉得不断解决新的挑战、不断重构才是编程的乐趣。

template<typename TFunc, typename ...TArgs>
struct bind_result {
  explicit bind_result(TFunc func, TArgs ...args)
    : func(std::move(func)), args(args...)
  {}

  template<typename ...TRealArgs>
  void operator()(TRealArgs ...real_args) {
    internal_call(typename gen_seq<sizeof...(TArgs)>::type(), real_args...);
  }

 private:
  TFunc func;
  std::tuple<TArgs...> args;

 private:

  template<int ...i, typename ...TRealArgs>
  void internal_call(seq<i...>, TRealArgs ...real_args) {
    func(std::get<i>(args)..., real_args...);
  }

};

测试样例1(绑定lambda -> OK):

int main() {
  auto f = [](int i, int j){
    std::cout << "i: " << i << "; j: " << j;
  };

  bind_result br(f, 1);
  br(2);
}

测试样例2(绑定function -> OK):

void f(int i, int j) {
  std::cout << "i: " << i << "; j: " << j;
}

int main() {
  bind_result br(&f, 1);
  br(2);
}

测试样例3(绑定method -> Err):

struct foo {
  void f(int i, int j) {
    std::cout << "i: " << i << "; j: " << j;
  }
};

int main() {
  bind_result br(&foo::f, 1);
  br(2);  // unlike other managed language, member func can't be simply invoked like
          // a function pointer, otherwise, you should invoke it with an instance 
          // variable.
}

测试样例4(绑定带返回值的callable对象 -> Err):

int main() {
  auto f_sum = [](int i, int j){
    return i + j;
  };
  bind_result f_sum_by_1(f_sum, 1);

  auto res = f_sum_by_1(2);  // f_sum_by_1 returns void.
}

标签:std,...,args,seq,int,bind,work
From: https://www.cnblogs.com/sherlockcai/p/17926878.html

相关文章

  • Excel poi 设置单元格格式 发现不可读内容 已修复的记录: /xl/worksheets/sheet1.xml
    Excelpoi设置单元格格式发现不可读内容已修复的记录:/xl/worksheets/sheet1.xml部分的问题(巨坑)1.先设置值,后设置样式。正确的是:先设置样式,后设置值。2.对象A的样式应用于对象B的样式,导致报错。 正确的是:对象A应用对象A的样式,对象B应用对象B的样式。privateHSSFWorkb......
  • ncnn的blob_vkallocator、workspace_vkallocator、staging_vkallocator区别
    ncnn::Extractor中有三个成员函数:voidset_blob_vkallocator(VkAllocator*allocator);voidset_workspace_vkallocator(VkAllocator*allocator);voidset_staging_vkallocator(VkAllocator*allocator);blob_vkallocator是一个用于blob数据的分配器,它可以为一些长期存储......
  • 修改buffersize测试stdio的读写效率
    #include<stdio.h>#include<stdlib.h>#include<unistd.h>intmain(intargc,char**argv){if(argc<2){fprintf(stderr,"faildUsage...\n");exit(1);}intbuffersize=atoi(argv[1]);cha......
  • spring:Exception in thread "main" java.lang.NoClassDefFoundError: org/springframe
     设置了父类框架<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.10.RELEASE</version><relativePath/><!--l......
  • WorkPlus企业即时通讯系统的领先者,提升沟通效率的利器
    在当今高度竞争的商业环境下,企业需要高效的内部沟通和协作才能保持竞争力。而企业即时通讯系统作为实现高效沟通的关键工具,成为了现代企业必备的组成部分。在诸多选择中,为何选择WorkPlus作为企业的首选即时通讯系统呢?接下来,让我们一起探讨一下WorkPlus的优势。作为企业即时通讯系统......
  • WorkPlus十年铸剑,成就千万级用户信赖与认可
    过去的十年中,WorkPlus在即时通讯和协作领域经历了一次又一次的挑战和探索。数百个功能点的丰富性、系统的稳定性以及千万级用户的信赖和认可,为WorkPlus赢得了声誉。让我们一起来了解WorkPlus的成功之路。WorkPlus的研发历程已经超过十年,经过上百次迭代,持续不断地完善和提升产品功能......
  • WorkPlus超级APP助力企业节省IT人力成本,实现快速移动化
    在信息化时代,移动应用已经成为企业发展的重要组成部分。然而,开发和维护原生客户端的成本却相对较高,需要大量的iOS、安卓和桌面端工程师。为了解决这一问题,WorkPlus作为一个功能完备的超级APP,为企业节约了大量的IT人力成本。通过使用WorkPlus,企业不需要额外雇佣iOS、安卓和桌面端工......
  • SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder"
     自动化框架中,从返回的json字符串中获取值,需要用jsonpath<!--https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path--><dependency><groupId>com.jayway.jsonpath</groupId><artifactId>json-path</artifactId><vers......
  • Graph Condensation for Graph Neural Networks
    目录概符号说明MotivationGCOND代码JinW.,ZhaoL.,ZhangS.,LiuY.,TangJ.andShahN.Graphcondensationforgraphneuralnetworks.ICLR,2022.概图上做压缩的工作.符号说明\(\mathbf{A}\in\mathbb{R}^{N\timesN}\),邻接矩阵;\(\mathbf{X}\in\mathbb{......
  • work4
    1、自建yum仓库,分别为网络源和本地源[root@srehostconf]#[root@srehostconf]#yumrepolistrepoidreponamemedia-appstream......