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

C++ 强制执行纯虚函数实现,可能使用不同的参数类型,c++,c++11,inheritance,polymorphism,system-design,C++,C++11,Inheritance,Polymorphism,System Design,我目前有一个基类Env,它是一个接口,我有几个派生类 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环境 {

我目前有一个基类Env,它是一个接口,我有几个派生类

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环境
{
公众:
//有问题的方法
虚拟std::元组步骤(const//what go here?//)=0;
virtual~Env();//虚拟析构函数
};
下面是一个示例派生类(header)

class山地车:公共环境
{
公众:
山地车();
std::元组步(const int)重写;
};
现在,设计是所有环境都必须从Env继承。但是,我希望强制所有环境来实现step()方法,这就是为什么基本环境中的step方法是纯虚拟的

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

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


如何使用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?
这将导致上面抛出
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;
};
类基{
虚拟无效步骤(std::string){throw runtime_error();}
虚空步骤(int){throw runtime_error();}
};
//只有这个类的子类
模板
类BaseHelper:公共基{
void step(T)override=0;//替换为纯虚拟
}
类DerivedString:公共BaseHelper{
void step(std::string s)override;//如果忘记,则实例化时发生编译器错误
};
类DerivedInt:公共BaseHelper{
无效步骤(int i)覆盖;
};

如果要通过基类指针或引用调用虚拟函数,则定义虚拟函数。出于任何其他原因这样做都是对语言的滥用

当然,您只能在确切知道签名的情况下调用函数。如果不这样做,那么就不会有调用,也不会定义函数

因此,在你的情况下:

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;
class环境{
公众:
事实上的
std::tuple
步骤(const//这里是什么?
//要在代码中传递给此函数的内容,
//如果您有环境指针或引用:
//pEnv->步骤(42);
//如果你没有理由打这样的电话,
//只是不要定义函数。
) = 0;

您如何使用它?如果另一个类有一个
std::string
作为该方法的参数,您将如何使用基指针调用step函数?因为该类也将从基类继承,并且step方法是虚拟的,所以我可以调用它。问题是如何实现这一点(确保派生类重写至少采用一种参数类型的step方法)仔细考虑。
base->step(something)
在你的代码中。如果base可以指向需要int的env,或者需要字符串的env,你可以传递什么类型的
东西?我不明白你的意思。我的基本指针应该是env*MountainCar。然后,我会执行env->step(1)。如果您知道最派生的类型-当然。但是您不需要虚拟函数,只需使用该派生类型。当您接收并将
Env*
作为函数参数时,您会怎么做。我确实希望通过基类指针调用它。除此之外,我还将其设置为纯虚拟,以便在所有派生类中强制实现。对于“事情…”,我的意思是,尽管派生类用不同的类型定义了这个函数,但函数重写仍然有效。@SridharThiagarajan我确实想通过基类指针调用它。那么你想传递什么类型的参数呢?“某种类型?”"不是有效答案。您需要命名它。如果选择cannon,请考虑参数值的来源。参数类型将取决于基指针指向的派生对象的类型。如果是BasePointer*stringclass,则有效输入为字符串等。@SridharThiagarajan参数类型将取决于派生对象的类型基指针指向。您不知道派生对象的类型。如果您知道,您将使用指向正确派生类型的指针而不是基指针。在您发布代码后很长时间内可以创建派生类型,但通常这是不可能的。那么参数从何而来?这还不足以说明“它的类型取决于这个或那个"。您需要知道类型。已知类型来自固定集,如果这有帮助的话。我喜欢RaiseNotImplemented错误解决方案,这在python中很常见,唯一的问题是我们没有得到编译时错误。当我们有纯虚拟函数时,我想当没有定义step方法时,我们会得到编译时错误。com在
bs->step(1)
上打桩时,如果不在
bi->step(1)上创建桩,则不可能在
bs->step(1)上打桩
,因为
bs
bi
具有相同的类型。如果它们没有相同的类型,那么您也可以有两个不同的基类。step调用上的编译可能是不可能的,但我说的是一个派生类根本不实现step的类现在,所有派生类都有不同的基类
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 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;