免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5867 | 回复: 16
打印 上一主题 下一主题

[C++] 咨询一个C++模板的问题…… [复制链接]

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-12-29 20:04 |只看该作者 |倒序浏览
假设我现在有个类storke,它对外提供服务接口通过仿函数实现,接口是这样的

  1. void stroke::operator()(size_t count, vec2 const vs[], float dists[] = 0);
复制代码
第三项是这样的:它可以通过前两项计算出来,但是,计算还是比较耗时的,因
此我希望可以当用户计算好的话,可以传进来,如果没计算好的话,stroke就自
行计算。

现在有个类 dist_calc,就是负责计算的,接口大概是这样:

  1. template <typename Conv>
  2. class dist_calc {
  3. public:
  4.   dist_calc(Conv& conv) : conv_(conv) {}
  5.   void operator()(size_t count, vec2 const vs[], float dists[] = 0) {
  6.     if (dists == 0)
  7.       dists = update_dist(count, vs);
  8.     conv_(count, vs, dists);
  9.   }
  10.   // ....
  11. };
复制代码
理论上,通过dist_calc包装,stroke肯定能获得正确的参数,问题是,我想通
过某种措施(比如继承模板神马的),让stroke“自动”拥有dist_calc的能力
,大概来想就是这样的:

  1. class stroke : public dist_calc<stroke> {
  2.   typedef dist_calc<stroke> base_type;
  3. public:
  4.   stroke(...) : base_type(*this) { }
  5.   void operator()(size_t count, vec2 const vs[], float dists[]) {
  6.     do_stroke(count, vs, dists); // implement
  7.   }
  8. };
复制代码
额,不过具体里面的细节就有点拎不清了,给点建议?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
2 [报告]
发表于 2013-12-29 20:29 |只看该作者
重载不行么。。。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
3 [报告]
发表于 2013-12-29 20:49 |只看该作者
回复 2# OwnWaterloo


    一来,重载的话,就没法处理dists是NULL的情况了……但是这一点都不重要……

麻烦的是,只要子类有任何一个函数存在,继承类所有同名不同签名函数都会被隐藏,所以没办法形成重载的效果吧……

主要目的是想能“自动地”让一系列类带上“自动距离计算”的功能,尽量在子类里面少写重复的参数判断代码……

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
4 [报告]
发表于 2013-12-29 22:03 |只看该作者
还是用这段代码就行

  1. template <typename Conv>
  2. class dist_calc {
  3. public:
  4.   dist_calc(Conv& conv) : conv_(conv) {}
  5.   void operator()(size_t count, vec2 const vs[], float dists[] = 0) {
  6.     if (dists == 0)
  7.       dists = update_dist(count, vs);
  8.     conv_(count, vs, dists);
  9.   }
  10.   // ....
  11. };
复制代码
规定使用者,不可以继承 dist_calc 就行了。
要继承,只能继承 Conv 类。

dist_calc 相当于调用 Conv类的一个代理。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
5 [报告]
发表于 2013-12-29 22:24 |只看该作者
  1. //this way?
  2. template <typename Derive>
  3. struct dist_calc
  4. {
  5.     typedef Derive host_type;
  6.     void operator()(size_t count, vec2 const vs[], float* dists = nullptr)
  7.     {
  8.         auto& self = static_cast<host_type&>(*this);
  9.         if ( dists == nullptr )
  10.           self.calc_dists( count, vs, dists );
  11.         self.full_impl( count, vs, dists );
  12.     }
  13.     void calc_dists( size_t count, vec2 const vs[], float* dists );
  14.     void full_impl( size_t count, vec2 const vs[], float* dists );
  15. };
复制代码

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
6 [报告]
发表于 2013-12-29 22:26 |只看该作者
上边的 calc_dists 和 full_impl 视情况放在 dist_calc 或者 stroke 中去。

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
7 [报告]
发表于 2013-12-29 22:38 |只看该作者
回复 1# starwing83

稍有些麻烦,但相对简单的办法就是re-introduce操作符函数名,在子类里加入这么一句:using base_type:perator(); 这样基类的操作符就可见了,达到了把基类操作符函数放到子类的候选的列表中的目的。
   

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
8 [报告]
发表于 2013-12-29 22:47 |只看该作者
回复 3# starwing83

还是有很多地方不明白。

先说重载。 假设:

  1. void DO(size_t count, vec2 const vs[], float dists[]) {
  2.       assert(dists);
  3.       // 实际干苦力的代码
  4. }
  5. float* DISTS(size_t count, vec2 const vs[]) {
  6.       // 通过count与vs计算dists的代码
  7.       return ...;
  8. }
复制代码
(BTW: 内存打算怎么释放。。。 比如dists = update_dist(count, vs);里面)
DO,DISTS只是表示这两段计算的代码片段。 为了方便说明而已。 至于它是否应真的应该是一个独立的函数、或者是操作符是另外的事情。
比如:

  1. void stroke::operator()(size_t count, vec2 const vs[], float dists[] = 0) {
  2.       if (!dists) dists = DISTS(count, vs);
  3.       DO(count, vs, dists);
  4.       // ...
  5. }
复制代码
的意思是如果打算将功能做成一个调用操作符, 并且dists选择一个默认参数然后在运行时判断就可以这么写。 DISTS和DO可以直接展开到stroke:: operator() 里面而并不出现在其他地方。

如果不支持重载的话:

  1. void f_with_dists(size_t count, vec2 const vs[], float dists[]) { DO(count, vs, dists); }
  2. void f_without_dists(size_t count, vec2 const vs[]) { float* dists = DISTS(count, vs); DO(count, vs, dists); /* ... */ }
复制代码
是最清晰的了吧? 为了方便调用、减少暴露接口的数量可能还会:

  1. void f_maybe_dists(size_t count, vec2 const vs[], float dists[]) { if (!dists) dists = DISTS(count, vs); DO(count, vs, dists); /* ... */ }
复制代码
至于它们究竟应该是什么名字, 究竟要实现/暴露哪些就需要权衡了。。。

而如果支持重载:

  1. void f(size_t count, vec2 const vs[], float dists[]) { DO(count, vs, dists); }
  2. void f(size_t count, vec2 const vs[]) { float* dists = DISTS(count, vs); f(count, vs, dists); /* ... */ }
复制代码
而默认参数反而显得很鸡肋:

  1. void f(size_t count, vec2 const vs[], float dists[] = 0);
复制代码
用户有没有计算好dists在编译时的各个调用点上就区分出来了:

  1. f(count,vs);
  2. f(count,vs,dists);
复制代码
除非这种编译时的选择不能继续“扩散”出去。


然后说继承。。。
>> 麻烦的是,只要子类有任何一个函数存在,继承类所有同名不同签名函数都会被隐藏,所以没办法形成重载的效果吧……
嗯。。。  要using。。。

>> 主要目的是想能“自动地”让一系列类带上“自动距离计算”的功能,尽量在子类里面少写重复的参数判断代码……
于是就不明白除了storke之外其他类到底想要做什么。。。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
9 [报告]
发表于 2013-12-29 23:10 |只看该作者
回复 8# OwnWaterloo


    恩,我说一下实际情况吧。

所谓路径,是指一系列的坐标点,形成的一个多边形,而对路径进行变换(比如说描边),则是一个函数,对这一系列点进行处理,产生另外一系列点。

从而,路径变换可以作为一个单独的仿函数存在,通过链接这些仿函数,来达到“路径处理流”的作用。这些仿函数其实是类模板,下一个处理(即continuation)就是模板的(第一个)参数。通过一次(可能很长)的声明,我们就能构建出一个路径处理流的单独的“函数”出来。

单纯说这里,设计应该是很清晰的,路径的处理函数很多,比如转换成虚线啊,描边啊,顶点变换啊,等等。

问题是,从算法上来说,有些路径处理可能依赖一个很昂贵的操作:计算各个点之间的距离。这个操作很可能在上个操作就因为上个操作的算法特性而被计算出来了,也可能就需要单独计算。

因此,路径处理就有了第三个可选参数,即每个点到下一个点的距离。

我希望做的事情是这样的,当声明一个新的路径处理器(类模板)的时候,我可以写少量的代码,甚至不写代码,而自动地对距离这个参数进行判断:如果是NULL,那么就自行计算,否则,用外界传进来的计算结果,目前的情况下,是每个路径处理器都必须“例行公事”地写上两句,就是你写的那个if,我在想怎么能通过某种技术,让这两句自动化,而不是每写一个路径处理器都得加这两句。

内存的话,内存是dist_calc管理的,无论是继承还是组合,都会随着路径处理器本身过作用域而跟着一起释放掉的,而对路径处理器的反复使用这个情况来看,其内存也是可以反复使用的(不会完全释放,直到过作用域)。

论坛徽章:
2
酉鸡
日期:2014-01-09 13:24:252015年亚洲杯之阿曼
日期:2015-02-13 00:43:51
10 [报告]
发表于 2013-12-29 23:11 |只看该作者
myworkstation 发表于 2013-12-29 22:38
回复 1# starwing83

稍有些麻烦,但相对简单的办法就是re-introduce操作符函数名,在子类里加入这么一句 ...


这是C++11新加入的语法吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP