Полиморфные функции с параметрами из иерархии классов - PullRequest
3 голосов
/ 13 апреля 2010

Допустим, у меня есть следующая иерархия классов в C ++:

class Base;
class Derived1 : public Base;
class Derived2 : public Base;


class ParamType;
class DerivedParamType1 : public ParamType;
class DerivedParamType2 : public ParamType;

И я хочу, чтобы полиморфная функция func(ParamType), определенная в Base, принимала параметр типа DerivedParamType1 для Derived1 и параметр типа DerivedParamType2 для Derived2.

Как бы это сделать без указателей, если это возможно?

Ответы [ 6 ]

2 голосов
/ 13 апреля 2010

Это побеждает цель полиморфизма;если Base предоставляет функцию Base::func(const ParamType&), то эта же функция (или ее переопределение) должна принять const ParamType& в Derived1.Вы можете предоставить перегрузку для func, которая специализируется на const DerivedParamType1&.

. Самое близкое к тому, что вы ищете, это предоставить такую ​​специализированную перегрузку, а затем сделать Derived1::func(const ParamType&) приватной.Обратите внимание, однако, что это нарушает полиморфизм.Смысл полиморфизма в том, что если вы можете вызывать функцию базового типа, то вы можете вызывать эту же функцию (с теми же параметрами) для любого класса, который ее наследует, что явно не так.

2 голосов
/ 13 апреля 2010

Вы не можете иметь Base :: func, принимающий различные параметры в зависимости от того, какой класс его наследует. Вам нужно будет что-то изменить.

Вы можете заставить их обоих использовать ParamType и обрабатывать неожиданный параметр любым механизмом, который вам нравится (например, генерировать исключение или возвращать код ошибки вместо void):

struct ParamType;
struct Base {
  void func(ParamType&);
}
struct Derived1 : Base {};
//...

Или шаблон по типу параметра, который они должны принимать:

struct ParamType;
struct DerivedParamType1 : ParamType {};
struct DerivedParamType2 : ParamType {};

template<class ParamT>
struct Base {
  void func(ParamT&);
};
struct Derived1 : Base<DerivedParamType1> {};
struct Derived2 : Base<DerivedParamType2> {};

Со вторым решением Derived1 и Derived2 не будут иметь общую базу и не могут использоваться полиморфно.

2 голосов
/ 13 апреля 2010

Вы ищете ковариантные параметры. Это невозможно в C ++, который поддерживает только ковариантные возвращаемые типы.

Это не просто упущение в языке. То, что вы пытаетесь сделать, на самом деле не имеет смысла. Представьте, например, что вы можете определить следующую настройку:

class Decoder { virtual void decode(Stream*); };
class Base64Decoder { void decode(Base64Stream*); };
class GZipDecoder { void decode(GZipStream*); };
...
Decoder* d = new Base64Decoder;
d.decode(new GZipStream("file.gz"));

Поскольку Decoder::decode() принимает любые Stream, последняя строка является допустимым кодом. Если правила виртуальных функций разрешают то, что вы хотите, Base64Decoder::decode будет передано GZipStream.

1 голос
/ 14 апреля 2010

Если они (класс оператора и тип параметра) являются отдельными понятиями, то они должны оставаться отдельными. reinterpret_cast или что-то в ваших переопределенных методах, но если они ортогональны, то делать то, что вы просите, не имеет смысла.

Если они на самом деле не разделены, сделайте это очевидным и покончите со всем понятием виртуальных функций, поскольку это не то, что вы хотите. Вы знаете тип объекта, который у вас есть, и знаете тип параметров, поэтому в этом случае нет ничего «виртуального» ни в чем.

class Base
{
public:
   class ParamType { }

   void DoSomething(const ParamType&); // called by derived classes as necessary
};

class Derived1 : public Base
{
public:
   class DerivedParamType1 : public ParamType { }

   void DoSomething(const DerivedParamType1&);
};

class Derived2 : public Base
{
public:
   class DerivedParamType2 : public ParamType { }

   void DoSomething(const DerivedParamType2&);
};
1 голос
/ 13 апреля 2010

Это называется двойная отправка , хотя это нужно будет делать с указателями на базовый класс, так работает полиморфизм. В C ++ двойная диспетчеризация не поддерживается напрямую, поэтому требуется некоторая работа.

0 голосов
/ 13 апреля 2010

Как говорит Стефаанв, это может быть достигнуто с помощью двойной отправки с дополнительной сантехникой:

#include <iostream>

class Derived1;
class Derived2;

class ParamType
{
public:
    virtual void invertFunc (const Derived1& deriv) const = 0;
    virtual void invertFunc (const Derived2& deriv) const = 0;
};

class DerivedParamType1;
class DerivedParamType2;

class Base
{
public:
    virtual void func (const ParamType& param) const = 0;

    virtual void func (const DerivedParamType1& param) const
    {
        throw std::runtime_error ("Can not accept DerivedParamType1");
    }

    virtual void func (const DerivedParamType2& param) const
    {
        throw std::runtime_error ("Can not accept DerivedParamType2");
    }
};

class Derived1 : public Base
{
public:
    void func (const ParamType& param) const
    {
        param.invertFunc (*this);
    }

    void func (const DerivedParamType1& param) const
    {
        std::cout << "Derived1::func (DerivedParamType1)" << std::endl;
    }
};

class Derived2 : public Base
{
public:
    void func (const ParamType& param) const
    {
        param.invertFunc (*this);
    }

    void func (const DerivedParamType2& param) const
    {
        std::cout << "Derived2::func (DerivedParamType2)" << std::endl;
    }
};

class DerivedParamType1 : public ParamType
{
public:
    void invertFunc (const Derived1& deriv) const
    {
        deriv.func (*this);
    }

    void invertFunc (const Derived2& deriv) const
    {
        deriv.func (*this);
    }
};

class DerivedParamType2 : public ParamType
{
public:
    void invertFunc (const Derived1& deriv) const
    {
        deriv.func (*this);
    }

    void invertFunc (const Derived2& deriv) const
    {
        deriv.func (*this);
    }
};


int main (int argc, char* argv[])
{
    ParamType* paramType = new DerivedParamType1;
    Base* deriv = new Derived1;

    deriv->func (*paramType);

    return 0;
}

Обратите внимание, что на самом деле здесь 3 прыжка (отправки), когда вы просили Base::func(ParamType) позвонить Derived1::func(DerivedParamType1). Если вас устраивает либо:

Base::func(ParamType) звонки DerivedParamType1::func(Derived1)

или

ParamType::func(Base) звонки Derived1::func(DerivedParamType1)

тогда вы можете устранить один из прыжков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...