我正在滥用C ++模板,我无法搞清楚。假设我有两种类型真的应该从基类型继承而来,但出于速度原因,我无法承担虚拟功能的开销(我已经对它进行了基准测试,虚拟调用会毁掉我的东西!)。 / p>
首先,这是我有的两个班级
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
和Class2
?那么处理文件和处理内存之间的根本区别是如此之大,以至于为不同类型的输入设置不同的类是有意义的(而不是仅仅重载构造函数并使其对于不同的输入具有不同的行为)。同样,我对此进行了基准测试,并且在特定情况下处理特殊情况要快得多,而不是在每个函数中都有case
s / if
。
所以我想做的是从初级开发人员那里隐藏很多这样的实现,我不希望他们必须创建三个不同的重载DoStuff
来处理不同的输入。理想情况下,我只是使用#defines
设置某种类型的回调,他们需要做的就是创建一个名为DoStuff
的类并重载()
运算符并使其具有仿函数做的工作。
我遇到的麻烦是执行工作的DoStuff
函数只是由<class C>
模板化,但C本身是由<class DataType>
模板化而且我无法弄清楚的一切如何以通用的方式传递一切。例如,我无法使用template <class C<DataType>>
或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
函数,让其他人编写CallBack函数。
任何想法?
编辑澄清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是什么,但对于基于文件的数组,在打开文件并查询标头以了解数组中的数据类型之前,我可能不知道数据类型。 / p>
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>
。编译器对此不满意。
答案 0 :(得分:3)
关注问题的初始部分(为什么你不只是使用继承):
通过基类进行编译时多态并通过派生类成员访问的常用方法是CRTP模式。
template <typename T>
class IInterface {
void DoStuff() {
void static_cast<T*>(this)->DoStuff()
}
};
class Class1 : IInterface<Class1> {
void DoStuff(){...}
}
这会解决您的问题吗?
修改强> 顺便说一句,我很高兴我可以提供帮助,但下次请尝试更多地构建你的问题。
我真的不知道你在问什么,所以这只是在黑暗中刺伤,基于你问题的前3行。 ;)
你永远不会真正解释你想要实现的目标,只能解释你的无功能解决方案的样子。开始说明问题,因为这是我们真正需要知道的。然后,您可以提供有关当前变通方法的详细信息。在发布代码时,添加一些上下文。 DoStuff()从哪里调用,为什么初级开发人员需要定义它们? (你已经这样做了,不是吗?)
初级开发人员首先会使用此代码做什么?
令人困惑的是你提供了特定的案例(1和2),但没有提供switch语句本身(//某事)
如果你试着让回答的人容易,你下次会得到更多(更好,更快)的答案。 :)
答案 1 :(得分:0)
关于“广义回调”的问题,您可以使用boost::function但基本上使用虚拟函数(它可能不是 - 但至少是类似的概念)所以您正在寻找的性能差异因为堆分配,所以boost :: function可能会慢一点。