禁止虚拟模板功能的解决方法

workaround for prohibition on virtual template functions?

本文关键字:解决 方法 功能 虚拟 禁止      更新时间:2023-10-16

我对C++的第一次尝试是构建一个音频合成库(EZPlug)。

该库的设计目的是便于设置互连音频生成器和处理器对象的图形。我们可以将EZPlugGenerators称为

所有处理器单元都可以接受一个或多个EZPlugGenerator作为输入。

对我来说,这些EZPlugGenerator上的所有配置方法都是可链接的,这一点很重要。换句话说,用于设置合成图的方法应该始终返回指向父对象的指针。这允许我使用一种语法,它非常好地显示了对象关系的嵌套性质,如下所示:

            mixer.addGenerator(
            a(new Panner())
            ->setVolume(0.1)
            ->setSource(
                a(new TriggererPeriodic())
                ->setFrequency(
                    v(new FixedValue(1), "envTriggerFreq")
                )
                ->setTriggerable(
                    a(new Enveloper())
                    ->setAllTimes(v(0.0001), v(0.05), v(0.0f, "envSustain"), v(0.01))
                    ->setAudioSource(
                        a(new SineWaveMod())
                        ->setFrequency(
                            a(new Adder())
                            ->addGenerator(a(new Adder()))
                            ->addGenerator(v(5000, "sineFreq"))
                            ->addGenerator(
                                a(new Multiplier())
                                ->addVal(v("sineFreq"))
                                ->addVal(
                                    a(new TriggererPeriodic())
                                    ->setFrequency(v("envTriggerFreq"))
                                    ->setTriggerable(
                                        a(new Enveloper())
                                        ->setAllTimes(0.1, 0.1, 0, 0.0001)
                                        ->setAudioSource(v(1, "envAmount"))
                                    )
                                )
                            )
                        )
                    )
                )
            )
        );

上面代码中的"a"answers"v"函数存储并返回对对象的引用,并处理检索和销毁它们

我怀疑我的C++方法看起来有点奇怪,但我发现这种语言实际上可以很好地适应我想要的编程方式。

现在回答我的问题

我想为所有EZPlugGenerator创建一个通用的超类,它可以接受要继承的输入。这个超类将有一个方法"addInput",它将被每个子类覆盖。问题来自于这样一个事实:我希望"addInput"返回一个指向子类实例的指针,而不是超类。

这是不可接受的:

EZPlugProcessor* addInput(EZPlugGenerator* generator)

因为这会返回一个指向超类实例的指针,而不是破坏我非常满意的可链接性的子层。

我试过这个:

template<typename T> virtual T* addInput(EZPlugGenerator* obj){

但编译器告诉我不能创建一个虚拟模板函数。

我不必在这里使用继承。我可以在每一个可以接受输入的EZPlugGenerator上实现"addInput"。看起来,将所有这些对象聚集在一个单亲类下将有助于明确它们都有共同点,并有助于强化addInput是将一个对象插入另一个对象的正确方法这一事实。

那么,有没有一种方法可以使用继承来规定一组类的每个成员都必须实现"addInput"方法,同时允许该方法返回指向子类实例的指针?

C++中的虚拟函数可以具有协变返回类型,这意味着您可以定义

virtual EZPlugProcessor *addInput(EZPlugGenerator* generator) = 0;

在基类中,然后是

struct MyProcessor : EZPlugProcessor {
    virtual MyProcessor *addinput(EZPlugGenerator* generator) { 
        ...
        return this;
    }
};

只要调用者知道(通过他们使用的类型)对象是MyProcessor,他们就可以将addInputMyProcessor特定的其他函数链接在一起。

如果你的继承层次结构有更多的层次,那么不幸的是,你有时会发现自己在写:

struct MySpecificProcessor : MyProcessor {
    virtual MySpecificProcessor *addinput(EZPlugGenerator* generator) {
        return static_cast<MySpecificProcessor*>(MyProcessor::addInput(generator));
    }
};

因为无法在EZPlugProcessor中指定addInput的返回类型为"指向对象的最派生类型的指针"。每个派生类都必须为自己"激活"协方差。

是的,C++已经提供了协变返回类型。
class Base
{
public:
    virtual Base* add() = 0 { return <some base ptr>; }
};
class Child : public Base
{
public:
    virtual Child* add() { return <some child ptr>; }
};

另一方面,没有人能够读取您的代码,因此您可能需要考虑是否有其他方法可以设置配置,而不是用C++编写LISP链接。