我稍微滥用了C++模板,但在弄清楚某些内容时遇到了麻烦。假设我有两种类型实际上应该从基本类型继承,但是出于速度原因,我负担不起虚函数的开销(我已经对其进行了基准测试,而虚调用对我来说是毁了一切!)。
首先,这是我的两个类(class)
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
实现相同的接口(interface),但是出于速度原因,我违反了一些规则。我想做的是在
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
或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是什么,但是对于基于文件的数组,在打开文件并查询 header 以了解数组中的数据类型之前,我可能不知道数据类型。
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>
。编译器对此感到不满意。 最佳答案
关注问题的最初部分(为什么不仅仅使用继承):
进行编译时多态并通过基类访问派生类成员的一种常见方法是通过CRTP模式。
template <typename T>
class IInterface {
void DoStuff() {
void static_cast<T*>(this)->DoStuff()
}
};
class Class1 : IInterface<Class1> {
void DoStuff(){...}
}
这样可以解决您的问题吗?
编辑:
顺便说一句,我很高兴能为您提供帮助,但是下次请尝试进一步整理您的问题。
我真的不知道您在问什么,所以根据您问题的前三行,这只是一个暗中的刺探。 ;)
您永远不会真正解释您要实现的目标,而只是解释无法正常工作的解决方法。首先说明问题,因为这是我们真正需要知道的。然后,您可以提供有关当前解决方法的详细信息。在发布代码时,请添加一些上下文。从哪里调用DoStuff(),为什么初级开发人员需要定义它们? (您已经这样做了,不是吗?)
首先,初级开发人员将如何处理此代码?
令人困惑的是您提供了特定的情况(1和2),但没有提供switch语句本身(//某物)
如果您尝试使接听者的回答变得容易,那么下次您将获得更多(更好,更快)的答案。 :)
关于c++ - 嵌套C++模板定义,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/860476/