软件开发>杂文>5后记

共享软件开发的点滴心得

[中文][English][日本語]

[机器人制作] [软件开发] [豆知识] [Squeak] [关于]

庙堂与江湖后记

刘新宇

2006年四月

 

庙堂与江湖草稿写完之后,有一个问题一直没有解决彻底,这个问题大致如下:

 

首先有一个基本的代码单元:

template<class T> struct Unit{

     void func(T v){ std::cout<<typeid(T).name()<<" "<<v<<"\n"; }

};

然后通过多重继承复用它:

template<class TList, template<class> class Op> struct ScatterNode;

template<template<class> class Op> struct ScatterNode<NullType, Op>{};

template<class T, class U, template<class> class Op>

struct ScatterNode< TypeList<T, U>, Op>

: public Op<T>, ScatterNode<U, Op>{

     template<class Arg> void call_func(Arg v){

          Op<Arg>* p=this;

          p->func(v);

     }

};

问题在于黑体部分的call_func,这个函数实在觉得有些多余,因为ScatterNode继承自其父类,所以父类的func方法自然也应该是ScatterNode的一部分。为什么不能直接调用呢?

 

实际上直接调用会出现问题,为了简化,把这段代码写在下面:

首先第一种情况是标准的函数重载:

struct Foo{

  void func(int x){ std::cout<<typeid(x).name()<<" "<<x<<"\n"; }

  void func(double x){ std::cout<<typeid(x).name()<<" "<<x<<"\n"; }

  void func(float x){ std::cout<<typeid(x).name()<<" "<<x<<"\n"; }

};

但是如果把上面三个函数分拆到一个继承体系中:

struct A{

  void func(int x){ std::cout<<typeid(x).name()<<" "<<x<<"\n"; }

};

struct B: public A{

  void func(double x){ std::cout<<typeid(x).name()<<" "<<x<<"\n"; }

};

struct C: public B{

  void func(float x){ std::cout<<typeid(x).name()<<" "<<x<<"\n"; }

};

就会有完全不同的结果,测试如下:

int main(int argc, char* argv[]){

  Foo foo;

  foo.func(1);

  foo.func(3.14);

  C c;

  c.func(1);

  c.func(3.14);

}

运行结果是:

i 1

d 3.14

f 1

f 3.14

说明分拆到3个类中,就不再是函数重载(即使参数类型不同),而变成了“隐藏”,最后一个类的floatfunc把前面两个intdouble的全部隐藏了。Cpper论坛上的网友指出了这点,并且详细解释了C++在进行名字解析时的规则[1]。如果希望在子类中避免隐藏父类的方法,需要使用using关键字。于是前面的设计就可以化简如下:

template<class TList, template<class> class Op> struct AbsUtil;

template<class T, template<class> class Op>

struct AbsUtil< TypeList<T, NullType>, Op>: public Op<T>{

    using Op<T>::func;

};

template<class T, class U, template<class> class Op>

struct AbsUtil< TypeList<T, U>, Op>:

public Op<T>, public AbsUtil<U, Op>

{

    using Op<T>::func;

    using AbsUtil<U, Op>::func;

};

这样就把那个call_func去掉了。使用方法也更直接:

typedef TypeList<int,

TypeList<float,

TypeList<double, NullType> > > TestList;

AbsUtil<TestList, Unit> util;

util.func(1);

util.func(3.14);

 

同时,如果Unit中的func是虚函数的情况,也能够化简为:

template<class T> struct VUnit{

    virtual void func(T x)=0;

};

template<class TList, template<class> class Op, class Super> struct Util;

template<template<class> class Op, class Super>

struct Util< NullType, Op, Super>: public Super{};

template<class T, class U, template<class> class Op, class Super>

struct Util< TypeList<T, U>, Op, Super>: public Op<T>, public Util<U, Op, Super>{

    void func(T x){ Op<T>::func(x); }

};

调用方式和以前没有区别,仍然是:

typedef AbsUtil<TestList, VUnit> BaseUtil;

typedef Util<TestList, Unit, BaseUtil> DerivedUtil;

BaseUtil* p = new DerivedUtil;

p->func(1);

p->func(3.14);

delete p;

 

最后要再次感谢cpper上的网友,指出隐藏“hide”这一概念,以及C++的名字解析规则。

 

参考书目:

[1] http://www.cpper.com/c/


[English][上一篇][下一篇][目录][主页][联系我们: liuxinyu95@gmail.com]