cast函数_C++函数调用解耦-V3

93a105f303795b56243cb19e29709ae5.png

距离上次写文章已经过去了一年。V2版本已经太监,现在的V3版本终于部分实现了该功能。

鉴于时间比较久远,再把需求赘述一下:

目标:实现可以存储任意函数的容器方案,此处“任意”指函数参数的个数和类型任意。

之前尝试了直接将std::function作为容器元素类型,但是这个方法没法解决函数参数类型不同的问题,所有的容器都要求元素类型一致,要满足这个条件,只能使用std::any类型了。std::function可以存储在std::any中,通过std::any_cast也可以将any变量转换成std::function。

首先,我们定义一个结构体AnyCallable,其最重要函数是 operator()运算符重载函数。

通过可变参数模板来支持任意数量、类型的函数参数调用。

template 

调用的方式如下所示,可以绑定函数,类成员函数,或者lambda函数。

void 

似乎一切都是那么风和日丽,心想事成,但是我想说的是,有许多坑隐藏在这些简单的调用中。

坑1:

const 

调用func_vecs[0]的时候,直接传入"hello"会丢出bad_anycast异常。看起来不可思议,为什么"hello"不能隐式转换成const std::string &?以前不是经常这么用么?让我们来看一下真实的类型。

首先要借助于boost::typeindex::type_id_with_cvr<T>().pretty_name()这个工具(可以输出带cvr修饰的类型信息),看一下在构造时存储在any中的类型,和函数调用时实际传入的参数类型。AnyCallble需要改写成如下样式以增加打印信息。

template 

测试1.1:

func_vecs

可以得到打印信息:

std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>
type list:                                                                                                                                                                                                          
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const& 

测试1.2:

func_vecs[0]("hello");

得到的参数类型如下:

type list:                                                                                                                                                                                                          
char const (&) [6]

发现了么?传入的参数类似和类构造时传入的function参数不一致!于是std::any在cast成function的时候就会转换失败,

这是因为std::any_cast并不能理解char const &可以转成const std::string&。

坑2:

当我对函数参数类型做修改,去掉&修饰符,惊奇地发现,调用func_vecs[0](a)失败,丢出bad_any_cast异常。

void 

让我们看一下参数类型,可以发现:

// 构造函数的输出
std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>  
// 函数调用时的输出
type list:                                                                                                                                                                                                          
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&

神奇么,传入的参数类型变成了 std::string const &, 为什么多了引用符?更奇怪的是下面语句可以正常运行。

func_vecs[0](std::move(a));

以上是我遇到的两个坑。第二个坑等我了解更多关于rvalue和move的信息后再填上吧。(以上代码参见 V3)

终于明白了坑2的原因,和std::bind有关。参考StackOverFlow

通过std::bind绑定的函数大体有两种传参的方式,一种是在初始化时就已经绑定好参数的,另一种是使用占位符std::placeholders::_*来定义的。

  • 前者取决于初始化时是否使用referencewrapper<T>,如std::ref或者std::cref。未使用reference_wrap则传入的变量类型就是T,否则就是T &。
  • 使用std::placeholders::_*来定义的变量,当函数被调用,如f(v1)时,实际传入的参数为std::forward<V1>(v1),实际传入的类型即为V1&&。这能解释为什么std::move可行。具体的move和forward内容留给下一篇展开来介绍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值