C++ 根据枚举值指定子函数
问题描述
我想要对一幅图像实现一个算法,并根据算法类型传递参数给这个算法。 (例如,我可能想要在串行或并行中计算一个曼德勃罗特集,但两种实现都使用相同的参数)。
所以,这里是表示分形类型的枚举: (我有意省略了 #include
, #pragmas
等…)
enum class FractalType
{
Mandelbrot,
SomeOtherFractal
};
我的摘要 FractalGenerator
类:
template <FractalType FT>
class
FractalGenerator
{
public:
virtual int computeFractal(Mat m, FractalParameters<FT> *fp) = 0;
};
一个用于曼德博集合参数的派生结构 MandelBrotParameters
:
struct MandelBrotParameters : FractalParameters<FractalType::Mandelbrot>
{
/* data */
};
这个类的实现还包括了一个用于Mandelbrot的实现:
MandelBrotSerial
:
class MandelbrotSerial : public FractalGenerator<FractalType::Mandelbrot>
{
public:
int computeFractal(Mat m, MandelBrotParameters *fp);
};
但是函数 MandelbrotSerial::computeFractal
没有覆盖其父抽象函数。
我该怎么解决这个问题?
解决方案
如果你的目标是使代码调用 computeFractal()
而不需要特定于特定的分形类型,你可以通过一个抽象的、非模板的基类来实现这一目标,用于你的各种参数类:
struct AbstractFractalParameters
{
// empty
}
template<typename T> struct FractalParameters<T> : public AbstractFractalParameters
{
// ...
}
template <FractalType FT> class FractalGenerator
{
public:
virtual int computeFractal(Mat m, AbstractFractalParameters *fp) = 0;
};
void SomeCallingCode(FractalGenerator * g, Mat m, AbstractFractalParameters * p)
{
g->computeFractal(m, p); // works, as long as (p) is pointing to the correct subclass for (g)
}
然后,您的computeFractal()
实现可以将接收到的AbstractFractalParameter *
指针向下转换为预期的特定于实现的子类类型。
当然,以这种方式执行有点危险,因为您依赖于调用代码传递指向正确类型的参数对象的指针;如果调用者弄错了,您要么需要处理错误条件(如果您的computeFractal()
实现使用dynamic_cast<>
),要么将出现未定义的行为(如果您的computeFractal()
实现使用static_cast<>
)。
您可能最好完全使用模板化的代码,完全避免虚方法/运行时多态。这意味着所有调用computeFractal()
的与分形类型无关的代码也需要进行模板化,不过是否是问题取决于您有多少调用代码以及其功能。
例如,注意下面的callComputeFractal()
函数演示了编译时多态,即main()(或任何人)可以以不同的生成器类型(在编译时指定)调用它,它将适用于任何类型。
#include <stdio.h>
class MandelbrotFractalGenerator;
class OtherFractalGenerator;
template<typename GenType> struct FractalParameters {};
// parameters appropriate for the mandelbrot generator
template<> struct FractalParameters<MandelbrotFractalGenerator>
{
FractalParameters(float p1, float p2) : mandelParam1(p1), mandelParam2(p2) {/* empty */}
float mandelParam1;
float mandelParam2;
};
// parameters appropriate for the other generator
template<> struct FractalParameters<OtherFractalGenerator>
{
FractalParameters(int p1, int p2) : otherParam1(p1), otherParam2(p2) {/* empty */}
int otherParam1;
int otherParam2;
};
class MandelbrotFractalGenerator
{
public:
MandelbrotFractalGenerator() {/* empty */}
int computeFractal(const FractalParameters<MandelbrotFractalGenerator> &fp)
{
printf("MandelbrotFractalGenerator::computeFractal mandelParam1=%f mandelParam2=%f\n", fp.mandelParam1, fp.mandelParam2);
return 0;
}
};
class OtherFractalGenerator
{
public:
OtherFractalGenerator() {/* empty */}
int computeFractal(const FractalParameters<OtherFractalGenerator> &fp)
{
printf("OtherFractalGenerator::computeFractal otherParam1=%i otherParam2=%i\n", fp.otherParam1, fp.otherParam2);
return 0;
}
};
// Example of a generator-type-agnostic templated function that can call methods on any generator-type
template<class FractalGeneratorType>
void callComputeFractal(FractalGeneratorType & generator, const FractalParameters<FractalGeneratorType> & params)
{
generator.computeFractal(params);
}
int main(int, char **)
{
MandelbrotFractalGenerator mandelbrotGen;
OtherFractalGenerator otherGen;
callComputeFractal<>(mandelbrotGen, FractalParameters<MandelbrotFractalGenerator>(1.0f, 2.0f));
callComputeFractal<>(otherGen, FractalParameters<OtherFractalGenerator>(3, 4));
return 0;
}