避免切换模板参数

时间:2013-12-08 06:25:23

标签: c++ templates

简化我有以下类层次结构:

class BaseVec {
  public:
    BaseVec() {};
    virtual ~BaseVec() {};

    virtual double get_double(int i) const = 0;
};

template<typename T>
class Vec : public BaseVec {
  public:
    Vec() { ... };
    ~Vec() { ... };

    T get(int i) const { ... };

    double get_double(int i) const {
      return get(i);
    };
};

在我的项目中,我反复使用以下形式的代码:

template<typename T>
double foo_template(void* p) {
  Vec<T>* v = reinterpret_cast<Vec<T>*>(p);
  // use v which involves calling get
  return result;
}

double foo(void* p, int type) {
  if (type == 0) {
    return foo_template<double>(p);
  } else if (type == 1) {
    return foo_template<int>(p);
  } else if (...) {
    // etc.
  } else {
    //unsupported type
  }
}

(我可以使用一个开关并使用枚举,或者首先将p投射到BaseVec,然后执行dynamic_cast,但逻辑则保持不变)

这不是理想的维护。例如,当我添加一个我想支持的附加类时,我必须为每个if-else-if块添加一个子句。

简化此操作的一种可能方法是将p投射到BaseVec*并使用get_double方法。但是,由于这种方法经常被调用,因此会导致性能不佳。此外,这根本不可能:有时我想调用get方法,因为返回的类型很重要。

我尝试了访问者模式,虽然这有一些优点,但它仍然意味着我必须为每个可能的模板参数编写一段单独的代码。

有没有办法让这段代码更容易维护?

PS:我没有(很多)控制foo的内容。 foo被外部程序调用(确切地说是R)。因此,我只能将通用指针,int,double和character vector传递给foo

PPS:欢迎提出更好的头衔。

3 个答案:

答案 0 :(得分:1)

首先,在转换为指向多态类的指针时不要使用reinterpret_cast。您可以编写一个简单的指针包装器,它允许您使用安全的转换操作符static_cast

template <class Type>
class PointerWrapper
{
public:

    PointerWrapper(Type* object);
    PointerWrapper& operator=(Type* object);
    Type* operator->();

protected:

    Type* object;

};

template <class Type>
PointerWrapper<Type>::PointerWrapper(Type* object) :
    object(object)
{
}

template <class Type>
PointerWrapper<Type>& PointerWrapper<Type>::operator=(Type* object)
{
    this->object = object;
}

template <class Type>
Type* PointerWrapper<Type>::operator->()
{
    return object;
}

现在你可以写:

typedef PointerWrapper<BaseVec> BaseVecPointer;

template<typename T>
double foo(void* p) {
    BaseVecPointer* vp = static_cast<BaseVecPointer*>(p);
    // ...
    // ... = (*vp)->get_double(...);
    // ...
    return result;
}

在此代码中使用了多态性功能,即调用函数get_double而不是调用get

但是如果你只想调用get而不是get_double,即你想根据运行时变量的值调用具有不同模板参数的模板函数,你可以使用以下方法:

enum FooTypes
{
    NoFooType = -1,
    DoubleFooType = 0,
    IntegerFooType = 1,
    // ...
    FooTypesCount
};

template<FooTypes fooType>
struct ChooseType
{
    static
    const FooTypes value = NoFooType;

    typedef void Type;
};

template<>
struct ChooseType<DoubleFooType>
{
    static
    const FooTypes value = DoubleFooType;

    typedef double Type;
};

template<>
struct ChooseType<IntegerFooType>
{
    static
    const FooTypes value = IntegerFooType;

    typedef int Type;
};

在这里,您应该为ChooseType变量的所有可能值编写类模板type的特化。 下面的代码描述了函数ChooseFoo,它选择了应该调用foo_template函数模板的专门化:

typedef double (*FooFunction)(void*);

template<FooTypes fooType>
FooFunction ChooseFooImpl(int type)
{
    if (type == fooType)
    {
        if (ChooseType<fooType>::value != NoFooType)
        {
            return foo_template<typename ChooseType<fooType>::Type>;
        }
        else
        {
            return NULL;
        }
    }
    else
    {
        return ChooseFooImpl<(FooTypes)(fooType - 1)>(type);
    }
}

template<>
FooFunction ChooseFooImpl<NoFooType>(int type)
{
    return NULL;
}

FooFunction ChooseFoo(int type)
{
    return ChooseFooImpl<FooTypesCount>(type);
}

这是foo函数实现:

double foo(void* p, int type)
{
    FooFunction fooFunction = ChooseFoo(type);

    if (fooFunction != NULL)
    {
        return fooFunction(p);
    }
    else
    {
        //unsupported type
        // ...
    }
}

答案 1 :(得分:0)

为什么不将foo_templat改为:

template<typename T>
double foo_template(Vec<T>*) {
  // use v which involves calling get
  return result;
}

foo

template<typename T>
double foo (Vec<T>* v )
{
return foo_template(v)
}

让参数演绎完成这项工作?

(你可以摆脱其中一个功能,但我想保持与原版一样接近)

答案 2 :(得分:0)

C ++中的自动分派通过virtua函数运行run-polymorphism,static_Cast的mnas使用static_type多态,但你需要知道要转换的类型。

使用不同的设计,避免void* ,您可以执行以下操作:

template<class Derived>
class static_polimorphic {};

template<class A>
A& upcast(static_polymorphic<A>& sa)
{ return static_cast<A&>(sa); }

template<class A>
const A& upcast(const static_polymorphic<A>& sa)
{ return static_cast<const A&>(sa); }

现在,你的课程就像是

class C1: public static_polymorphic<C1>
{
 ....
};

class C2: public static_polymorphic<C2>
{
 ....
};

然后将多态性应用为

template<class A> 
void function(const static_polymorphic<A>& sa)
{
   A& a = upcast(sa);
   a.methods();
   ...
}

换句话说,类型不再表示基本成员变量,而是表示基本模板参数。

另请注意,作为派生类型区分的基础,常见功能不会是虚拟的。您可以完全避免基于运行时的多态,除非您必须将不同的运行时类型创建的对象存储到同一个容器或集合中。

为此,您可以使用第二个带有抽象虚函数的非带温基,作为派生类中的一个“启动器”。 (可能最好使用运行时多态的一个作为第一个基础,以简化运行时指针转换,因为没有偏移量)