C++怎么构建 key 是 string, value 是函数的 map

2018-02-20 16:17:29 +08:00
 fyyz

C++怎么构建 key 是 string,value 是函数的 map

网上查了下,用 std::function 可以表示一个函数对象,目前我写出来的类型是这样的:

std::map<
    std::string, 
    std::function<
        std::string(param_1&, param_1&)
    >
>
handle_map;

这样写对吗?

还有,这种 map 应该怎么 insert 数据进去啊,insert 的时候 std::function 对象是要用 std::bind 构造出来丢进去吗?

5113 次点击
所在节点    C
47 条回复
gnaggnoyil
2018-02-21 16:49:48 +08:00
@snnn 我不太清楚贵司业务注重哪块,不过对于我而言,函数指针不能放闭包,光这一点在很多时候就足以否决它了.就连 Scheme 都有 call/cc 呢,就别假装和 Haskell 多亲近了.至于说额外用个 struct 包起来什么的……都封装到这种地步了还不如 std::function 一把梭.当然确实 C++的整个闭包体系面对多线程环境对象的生命周期问题确实有点挫,但是无脑 std::shared_ptr 目前也将就够用,更何况函数指针面对这种情况只能使问题更加混乱——用裸指针管理好多线程对象生命周期的问题谁敢说是"简单"的?反正我认为这至少不比搞清楚右值引用和移动语义更简单——而且大不了无脑完美转发,撑死多写几个重载.又不是要写 extern "C"的接口,搞的那么简陋干嘛……

而且 C++一来不一定是编译的,比如 Jypiter notebook ——是的没错这货就在最近加入了对 C/C++的支持;二来就算是编译的也不一定是编译到机器码,比如 emsrcipten.js;三来就算是编译到机器码也不保证源码和目标机器码有直接对应关系,只要符合 as-if rule 编译器心情好直接就给你内联了都说不定:GCC 老早就能对虚函数调用进行内联分析了,函数指针不可能就完全两眼一抹黑;四来不同平台的机器码显然又不一样.基于以上理由"观察编译后的目标代码来分析源码"最多只能算是特定情况下的辅助源码分析手段,而大部分情况下要照着语言的语义和库的文档进行分析(当然 C 和 C++在这里一坨坨的未定义行为对这种分析方法造成了非常大的阻碍,不过良好的编码习惯还是能一定程度上减轻其影响).而论及库的语义的话 std::function 并不比函数指针复杂多少——甚至它作为一个类型擦除的容器要比泛泛而谈的仿函对象要更简单.就算是编译错误,错误还能长到哪里去?反正比那些模板满天飞的库(比如 boost.spirit, boost.spirit, boost.spirit 和 boost.spirit)的编译错误短多了,用多了也就熟了.

C++确实在功能强大程度和易用性等多方面上有很多欠缺,甚至在可见的将来很多欠缺难以有解决的可能.不过除非特殊情况特殊考虑,否则我不认为回滚到 C 上是个更好的选择.
p64381
2018-02-21 17:05:40 +08:00
@ipwx 不用铺垫什么东西啊,把结构体里面加个成员就行了啊

typedef void (*CALL_BACK)(void*);
struct callback{
CALL_BACK cb;
void *cb_param;
char name[];
};

然后这样写
struct callback *p = xxxxx;
p->cb(p->cb_param);

或者干脆一些
p->cb(p);
ipwx
2018-02-21 17:32:05 +08:00
@p64381 其一,如果你用结构体,需要 malloc。那什么时候析构?在复杂的并发程序(比如 actor framework 里面),你必须依赖内存托管,因为这个函数的生命周期是框架负责的,而不是你程序员负责的。用 std::function,那么只要你绑定给它的参数有正确的垃圾回收和拷贝机制(比如正确的拷贝构造,或者智能指针比如 std::shared_ptr ),那么 std::function 就会遵循这套机制。

其二,std::function 可以借助 bind 在普通函数上绑定临时参数,比如从一个 (int,int,std::string) 特化一个 (int,int)。当然你可以说,我用结构体存参数啊?这不对,因为我的接受者只接受 (int,int),连结构体都不接受,你哪里传进去呢?你只能特例化一个 (int,int) 函数,想办法把 std::string 和这个特例化参数绑定…… 说实话我不太清楚该怎么用 C 做这件事情,除非你说服那个接受者重新写一个接受 (int,int,struct Context*) 的一套东西出来。

更进一步地,std::function 和 std::bind 的价值在于给所有 C++ 的用户,类库的撰写者、类库的使用者,提供了一个统一的抽象接口。它解耦了这两个角色,类库设计者只要关注 (int,int),而类库使用者不受 (int,int) 这个参数签名的限制。不要有附加的 struct,不许要处理垃圾回收。只要符合 std::function 这套接口的标准,管你是函数指针,是函数对象( struct + operator()),是对象的某个成员函数,是类的静态函数,还是通过 std::bind 诱导出的 partial function,又或是局部的 lambda,反正对于接受者都一样没区别。

新时代的 C++ 适合大型工程,乃至于开源世界各种风格迥异的类库,这是 C++ 这套的价值所在。这些都是石器时代的那套不能解决的问题。
- - - -

C 那套不是完全不好,比如 ABI 统一,一个二进制库永流传。C++ 这套在这方面是有缺陷的,C++ 的 ABI 被人长期诟病,std::function 跨编译器使用可能会出现各种问题。但是这套在开源世界里面其实不成问题,你完全可以用统一的编译器进行编译。反而是 C 那套,没有一套统一的兼容层解耦调用者和类库设计者,所以必须自己处理所有事情。类库就很难写了。

我只能说到这里了。我知道你说的 struct 那套,我也用过 C,而且好几年前还沉迷过,觉得 C++ 太臃肿,编译出来的程序太大,编译太慢,还是 C 好。但是后来用过 c#、用过 python、回头好好学了学 c++(真是太多东西了,编译器方面比如模板元编程,enable_if,再比如各种 boost 库),又用过 scala。用过这么多东西,我只能说 C 真的表达力比其他所有语言弱一大截。很多设计方法你必须借助高级特性才能写得出来,而且对于 C++ 而言新特性才出来不久,可能还不好写。这会迫使你转投比如 scala akka。不过学了这么多,可选择余地也大得多,我现在写个什么玩意儿,也能就着需求挑语言了。因为不同语言写同样的东西,顶层设计可能差异巨大,而这种巨大的差异也会带来巨大的工作量的区别。

你可能不知道我说的一些东西,包括 actor framework 之类的。如果有时间强烈推荐你去看看。目前成熟的 actor framework 我觉得非 scala akka 莫属。你学过之后绝对会在写并发程序方面有不一样的体会的,会得到弥足珍贵的思想的。
wizardforcel
2018-02-21 17:39:58 +08:00
@snnn 用 c++就好好用,否则就滚去用 c,别搞得不伦不类的。
Akiyu
2018-02-21 18:05:08 +08:00
c++ primer 上面有,你网上也能查到相关的代码
问之前先自己去实验实验,你问的问题试试也就知道了。
你真的是想来问问题的么?
看看楼上,引了一堆战
innoink
2018-02-21 20:20:52 +08:00
std::bind 不是必须,自己查 std::function 的构造函数

其他的自行查阅:

http://en.cppreference.com/w/cpp/container/map
jasonlz
2018-02-22 11:49:45 +08:00
用 lambda

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/431350

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX