我有点滥用 C++ 模板,但我无法弄清楚一些事情。假设我有两种确实应该从基本类型继承的类型,但是出于速度原因,我无法承受虚函数开销(我已经对其进行了基准测试,而虚调用毁了我的一切!)。
首先,这是我的两个课程
template<class DataType> class Class1
{
//Lots of stuff here
}
template<Class DataType> class Class2
{
//The same stuff as in Class1, but implemented differently
}
在典型的 oo 设计中,Class1
并且Class2
会继承自IInterface
并且我可以有一个看起来像这样的函数
DoStuff(IInterface& MyInterface)
{
}
但是我做不到,所以我做了这个
template <class C>
DoStuff(C& c)
{
}
我知道这并不漂亮,因为(在编译器级别)没有任何东西可以强制执行Class1
并Class2
实现相同的接口,但是出于速度原因,我违反了一些规则。
我想做的是在 上创建一个回调函数DoStuff
,但我不知道如何使它与模板一起工作(特别是因为那里隐藏着。
例如,这现在有效
DoStuff(char* filename)
{
switch (//figure out the type i need to make)
{
case 1: return DoStuff(Class1<int>(filename));
case 2: return DoStuff(Class1<double>(filename));
}
}
template<class DataType>
DoStuff(DataType* pdata)
{
return DoStuff(Class2<DataType>(pdata));
}
template<class C>
DoStuff(C c)
{
c.Print();
}
现在我知道你在问,为什么要使用Class1
and Class2
?好吧,处理文件和处理内存之间的根本区别是如此之大,以至于为不同类型的输入使用不同的类是有意义的(而不是仅仅重载构造函数并让它对不同的输入表现不同)。同样,我确实对此进行了基准测试,在他们自己的类中处理特殊情况比在每个函数中都有case
s/ s快得多。if
所以我想做的是对初级开发人员隐藏很多这种实现,我不希望他们必须创建三个不同的重载DoStuff
s 来处理不同的输入。理想情况下,我只需设置某种类型的回调,#defines
他们需要做的就是创建一个名为的类并重DoStuff
载运()
算符并让函子完成工作。
我遇到的麻烦是,DoStuff
完成这项工作的函数只是模板化,<class C>
但 C 本身是模板化的<class DataType>
,我无法弄清楚如何以通用方式传递所有内容。例如,我不能使用template <class C<DataType>>
or template<template< class DataType> class C>
。它只是不会编译。
有没有人有一个很好的技巧来使用这个嵌套的模板类进行通用回调,无论是函数还是仿函数(我不在乎)?基本上我想要一些东西,我可以编写一个不关心存储数据的类的通用函数,并让一个最常见的函数调用它,以确定要使用哪个类。
BigSwitch(CallBack,Inputs)
{
switch(//something)
{
case 1: return CallBack(Class1<Type>(Inputs))
case 2: return CallBack(Class2<Type>(Inputs))
}
}
这样我可以编写一个BigSwitch
函数并让其他人编写回调函数。
有任何想法吗?
编辑以澄清 Jalf:
我有两个非常相似的类,Class1
它们Class2
代表基本相同类型的数据,但是数据存储有很大不同。为了使其更具体,我将使用一个简单的示例:Class1
是一个简单的数组,Class2
看起来像一个数组,但不是存储在内存中而是存储在文件中(因为它太大而无法放入内存)。所以我现在就给他们MemArray
打电话FileArray
。所以假设我想要数组的总和。我可以做这样的事情
template <class ArrayType, class ReturnType>
ReturnType Sum(ArrayType A)
{
ReturnType S=0;
for (int i=A.begin();i<A.end();++i)
{
S+=A[i];
}
return S;
}
但是现在,我需要一种将真实数据加载到数组中的方法。如果它是一个基于内存的数组,我会这样做
MemArray<DataType> M(pData);
如果它是文件格式,我会这样做
FileArray<DataType> F(filename);
并且这两个调用都是有效的(因为编译器在编译时生成两个代码路径)
double MS=Sum<MemArray<DataType>,double>(M);
double FS=Sum<FileArray<DataType>,double>(F);
所有这些都假设我知道 DataType 是什么,但是对于基于文件的数组,我可能不知道数据类型,直到我打开文件并查询标题以了解数组中的数据类型。
double GetSum(char* filename)
{
int DataTypeCode=GetDataTypeCode(filename);
switch (DataTypeCode)
{
case 1: return Sum<FileArray<int>,double>(FileArray<int>(filename));
case 2: return Sum<FileArray<double>,double>(FileArray<double>(filename));
}
}
template <class DataType>
double GetSum(DataType* pData)
{
return Sum<MemArray<DataType>,double>(MemArray<DataType>(pData));
}
所有这些都有效,但它需要编写两个重载GetX
函数和一个X
函数来完成我想做的所有事情。除了它调用的GetX
函数之外,这些函数每次基本上都是相同的代码。X
所以我希望能够写出类似的东西
double GetX(CallBackType X, char* filename)
{
int DataTypeCode=GetDataTypeCode(filename);
switch (DataTypeCode)
{
case 1: return X<FileArray<int>,double>(FileArray<int>(filename));
case 2: return X<FileArray<double>,double>(FileArray<double>(filename));
}
}
template <class DataType>
double GetX(CallBackType, DataType* pData)
{
return X<MemArray<DataType>,double>(MemArray<DataType>(pData));
}
这样我就可以打电话
GetX(Sum,filename)
然后当其他人想要添加一个新函数时,他们需要做的就是编写函数并调用
GetX(NewFunction,filename)
我只是在寻找一种方法来编写我的重载GetX
函数和我的X
函数,以便我可以从实际算法中抽象出输入/存储的方式。通常,这不是一个难题,只是我遇到了麻烦,因为该X
函数包含一个模板参数,该参数本身是模板化的。template<class ArrayType>
也有一个隐含的隐藏ArrayType<DataType>
在那里。编译器对此不满意。