我稍微滥用了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设计中,Class1Class2将从IInterface继承而来,我可以拥有一个看起来像这样的函数
DoStuff(IInterface& MyInterface)
{
}

但是我做不到,所以我做到了
template <class C>
DoStuff(C& c)
{
}

我知道这不是很漂亮,因为没有任何东西(在编译器级别)可以强制Class1Class2实现相同的接口(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();
}

现在我知道你在问,为什么要使用Class1Class2?好了,处理文件和处理内存之间的根本区别是如此之大,以至于对于不同类型的输入使用不同的类是有意义的(而不是仅仅重载构造函数并使它对于不同的输入具有不同的行为)。再次,我做了基准测试,在他们自己的类中处理特殊情况比在每个函数中都使用caseif更快。

因此,我想对初级开发人员隐藏很多此类实现,我不希望他们必须创建三个不同的重载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:

我有两个非常相似的类Class1Class2,它们表示基本上相同的数据类型,但是数据存储区却大不相同。为了更具体,我将使用一个简单的示例:Class1是一个简单的数组,Class2看起来像一个数组,但是不是存储在内存中而是存储在文件中(因为它太大而无法容纳在内存中)。因此,我现在将它们称为MemArrayFileArray。假设我想要数组的总和。我可以做这样的事情
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/

10-11 22:34
查看更多