避免打开模板参数
Avoid switching on template parameters
简化后的类层次结构如下:
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
方法,因为返回的类型很重要。
我尝试了访问者模式,尽管这有一些优点,但这仍然意味着我必须为每个可能的模板参数编写一段单独的代码。
有没有什么方法可以让代码更容易维护?
附言:我对foo
中的内容没有(太多)控制权。foo
被外部程序调用(确切地说是R)。因此,我只能将通用指针、int、doubles和字符向量传递给foo
。
PPS:也欢迎对更好的标题提出建议。
首先,在转换到多态类的指针时不要使用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;
};
在这里,您应该为type
变量的所有可能值编写类模板ChooseType
的专门化。以下代码描述了函数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
// ...
}
}
为什么不将foo_templat
e更改为:
template<typename T>
double foo_template(Vec<T>*) {
// use v which involves calling get
return result;
}
CCD_ 23为
template<typename T>
double foo (Vec<T>* v )
{
return foo_template(v)
}
让论证推理来完成工作?
(你可能可以去掉其中一个函数,但我想保持与原始函数的接近)
C++中的自动调度通过virtuala函数实现运行时多态性,通过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();
...
}
换句话说,类型不再表示为基成员变量,而是由基模板参数表示。
还要注意的是,作为派生类型所区分的基础,公共函数将不会被视为虚拟的。您可以完全避免基于运行时的多态性,除非您必须将不同的运行时类型创建的对象存储到同一个容器或集合中。
为此,您可以使用第二个具有抽象虚拟函数的非临时基作为派生类中的"启动器"。(最好使用运行时多态性作为第一个基础,以简化运行时指针转换,因为不会有偏移)
- 如何反转整数参数包
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 函数调用中参数的顺序重要吗
- 部分定义/别名模板模板参数
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 使用不带参数的函数访问结构元素
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 如何使用Luacneneneba API正确读取字符串和表参数
- 在派生函数中指定void*参数
- 视图中的参数推导失败:take_while
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 使用指向成员的指针将成员函数作为参数传递
- 没有名称的C++模板参数
- 如何将enable-if与模板参数和参数包一起使用