强制执行纯虚函数实现,可能使用不同的参数类型

Enforce pure virtual function implementation, perhaps with different argument type

我目前有一个Base Class Env,它是一个接口,我有几个派生的classes。

class Env

{
    public:

        //method in question
        virtual std::tuple<Eigen::VectorXf,float,bool,std::string> step(const //what goes here?//)=0;

        virtual ~Env(); //virtual destructor
};

导出一个例子class如下(header)

class MountainCar: public Env

{
    public:

        MountainCar();
        std::tuple<VectorXf,float,bool,std::string> step(const int) override;

};

现在的设计是所有的环境都要继承自Env。但是,我想强制所有环境都实现step()方法,这就是为什么base Env中的step方法是纯虚的。

但是,每个派生的 Env 都可以在 step 方法中采用不同的参数类型,这应该是一个有效的覆盖(这些有效类型来自一个固定的已知集合)例如,mountain car 用一个 int 参数定义它。另一个环境 CartPole 将 VectorXf 作为步骤参数。

最初,我将基础 class 设为带参数 U 的模板 class,并将 U 传递给 step 方法。然后,派生的 classes 用于继承模板实例化,例如继承自 Env 的 MountainCar。但是,问题是所有派生的 classes 都继承自基 class 的不同实例化,我不能再使用公共基指针进行多态性。

如何设计具有 C++11 功能的系统?

你的前提没有多大意义。让我们想象一下这是可能的:

class Base {
    virtual void step(/* something */);
};
class DerivedString : public Base {
    void step(std::string) override;
};
class DerivedInt : public Base {
    void step(int) override;
};

你想让它做什么?

std::unique_ptr<Base> bs = std::make_unique<DerivedString>();
bs->step(1);  // compiler error? runtime error?

std::unique_ptr<Base> bi = std::make_unique<DerivedInt>();
bi->step("one");  // compiler error? runtime error?

如果您的答案是 "compiler error",那么您应该删除虚函数和基函数 class,因为它们没有提供任何价值。

如果你的答案是 "runtime error" 并且你有 C++17,你可以使用 std::any:

class Base {
    virtual void step(std::any);
};
class DerivedString : public Base {
    void step(std::any v) override {
        step(std::any_cast<std::string>(v));
    }
    void step(std::string s);
};
class DerivedInt : public Base {
    void step(std::any v) override {
        step(std::any_cast<int>(v));
    }
    void step(int i);
};

这将导致 std::bad_any_cast 被抛出上面。


如果你没有 C++17,并且你事先知道类型集,那么你可以预先声明函数的每个重载:

class Base {
    virtual void step(std::string) { throw runtime_error(); }
    virtual void step(int) { throw runtime_error(); }
};
// only subclass from this
template<typename T>
class BaseHelper : public Base {
    void step(T) override = 0; // replace with pure-virtual
}
class DerivedString : public BaseHelper<std::string> {
    void step(std::string s) override;  // compiler error when instantiated if forgotten
};
class DerivedInt : public BaseHelper<int> {
    void step(int i) override;
};

如果您想通过基class 指针或引用 调用它,您可以定义一个虚函数。出于任何其他原因这样做都是滥用语言。

自然地,您只能在确切知道签名的情况下才能调用函数。如果不这样做,就不能调用,也不能定义函数。

所以在你的情况下:

class Env {
    public:
       virtual 
         std::tuple<Eigen::VectorXf,float,bool,std::string> 
           step(const // What goes here?
                      // The thing YOU want to pass to this function in YOUR code,
                      // if you have an Env pointer or reference:
                      //     pEnv->step(42);
                      // If you don't have a reason to make such a call,
                      // just don't define the function.
               ) = 0;