можно ли изменить тип возврата при переопределении виртуальной функции в C++?

я сталкиваюсь с проблемами переопределения виртуальных функций, на самом деле, речь идет о hessian (протокол веб-службы).

он имеет объект базового класса и некоторые производные классы : Long,Int,String,...,все производные классы не имеют виртуальной функции "value"

   class Object  
   {  
     ...    
   };  


   class Long :public Object  
   {  
       ...  
   public:  
       typedef long long  basic_type;  
       basic_type value(){return value_;}  
   private:  
       basic_type value_;  
       ...  
   };  


   class Int :public Object  
   {  
      ...  
   public:  
       typedef int basic_type;  
       basic_type value(){return value_;}  
   private:   
       basic_type value_;  
       ...  
   };  

Теперь я хочу добавить функцию ,скажем, toString ,который может преобразовать объект в строку:

Object *obj  = ...
cout<<obj->toString();

Если я могу изменить функцию value на virtual, мне нужно только написать функцию toString в Object, else, мне нужно написать виртуальную функцию toString и переопределить эти функции во всех производных классах.

   class Object  
   {  
       virtual Type value(); // It seemed that I can't write a function like this,because the Type is different for different derived classes  


       std::string toString()  
       {  
           some_convert_function(value());  
       }  

   };  

но я не могу написать функцию виртуального значения, потому что возвращаемое значение не может быть переопределить.

есть ли хорошее решение для этого вопроса?

спасибо

6 ответов


нет, вы не можете написать toString в Object с помощью виртуальной функции "value" и переопределить возвращаемый тип. Однако вы можете написать виртуальную toString и с помощью шаблона программирования трюк выполнить почти то же самое.

class Object
{
public:
  virtual std::string toString();
}


template < class ValueType >
class BasicType : Object
{
  public:
  typedef ValueType basic_type;
  basic_type value() { return value_; }

  std::string toString()
  {
    return some_convert_function( value_ );
  }

  private:
  basic_type value_;
}

typedef BasicType<long long> Long;
typedef BasicType<int>       Int;

можно изменить тип возвращаемого при переопределении виртуальной функции в C++?

только очень ограниченным способом, в этом (необработанном) указателе или ссылочном типе возврата может быть ковариантным.

есть ли хорошее решение для этого вопроса?

Ну, есть два довольно хороших решения и одно немного плохое решение.

Я даю вам немного плохое решение. Одна из причин, что я даю, это легко понять, или по крайней мере, это довольно легко "копировать и изменять", даже если вы не совсем понимаете это. Другая причина заключается в том, что одно из хороших решений требует некоторого обширного общего механизма поддержки, который здесь нет места для обсуждения, а другое хорошее решение (то, которое, на мой взгляд, лучше всего почти во всех отношениях)-это то, что, по крайней мере, когда я представил такое решение, автоматически получил downvotes и только это, здесь так. Думаю, это цена, которую нужно заплатить за разнообразие здесь, которое разнообразие-очень хорошая вещь : -) но, к сожалению, это означает, что нет смысла предлагать действительно хорошие вещи, тогда я был бы до отрицательной репутации.

в любом случае, код, основанный на доминировании в виртуальном наследовании; это примерно то же самое, что наследовать реализацию интерфейса в Java или C#:

#include <iostream>
#include <string>
#include <sstream>

//--------------------------------------- Machinery:

class ToStringInterface
{
public:
    virtual std::string toString() const = 0;
};

template< typename ValueProvider >
class ToStringImpl
    : public virtual ToStringInterface
{
public:
    virtual std::string toString() const
    {
        ValueProvider const&    self    =
            *static_cast<ValueProvider const*>( this );
        std::ostringstream      stream;
        stream << self.value();
        return stream.str();
    }
};

//--------------------------------------- Usage example:

class Object  
    : public virtual ToStringInterface
{  
    // ...    
};  

class Long
    : public Object
    , public ToStringImpl< Long >
{  
public:  
   typedef long long  BasicType;  
   Long( BasicType v ): value_( v ) {}
   BasicType value() const { return value_; }  
private:  
   BasicType value_;  
};  

class Int
    : public Object
    , public ToStringImpl< Int >
{  
public:  
   typedef int BasicType;  
   Int( BasicType v ): value_( v ) {}
   BasicType value() const { return value_; }
private:   
   BasicType value_;  
}; 

int main()
{
    Object const& obj = Int( 42 );
    std::cout << obj.toString() << std::endl;
}

если Long и Int классы etc. очень похожи, как они кажутся, рассмотреть вопрос об определении только одного шаблона класса, или, возможно наследовать от специализаций такого шаблона (это также может помочь избежать ошибок, так как уменьшает избыточность).

редактировать: теперь я вижу,что вы приняли ответ, который, по сути, является моим последним предложением о шаблонах. Это означает, что я ответил на поставленный вопрос (решение для разных классов), в то время как вы имели в виду что-то менее общее. Ну что ж.

Cheers & hth.,


к сожалению вы не можете перегрузка функций в C++ возвращаемое значение. Что вы могли бы сделать, если у вас есть соответствующие some_convert_function на месте для всех типов, которые вам нужны, было бы создать бесплатную функцию шаблона, которая выглядит примерно так:

template<typename T>
std::string toString(T const& t)
{
  return some_convert_function<T>(t);
}

вы не можете переопределить функцию с другим типом возврата; самое близкое, что вы можете сделать, это скрыть функцию в Родительском с другим в производном классе. Но это не то, что вы хотите, потому что эти две функции будут разными, совершенно не связанными.

вы были правы, предполагая, что вам нужно будет создать новый toString функция в каждом производном классе-вот что такое полиморфизм.


Что касается комментария @MerickOWA, вот еще одно решение, которое не требует дополнительного механизма шаблона.

поскольку вы намеревались иметь виртуальный метод " value ()", который вам нужно было реализовать во всех классах, я расширил эту идею (обычно в таких рамках у вас есть много подобных" базовых " методов, поэтому я использовал макрос, чтобы написать их для меня, это не требуется, это просто быстрее и менее подвержено ошибкам.

#include <iostream>
#include <string>
#include <sstream>

struct Object
{
   std::string toString() const { std::ostringstream str; getValue(str); return str.str(); }
   virtual void getValue(std::ostringstream & str) const { str<<"BadObj"; }
};

// Add all the common "basic & common" function here
#define __BoilerPlate__     basic_type value; void getValue(std::ostringstream & str) const { str << value; }
// The only type specific part
#define MAKE_OBJ(T)         typedef T basic_type;      __BoilerPlate__

struct Long : public Object
{
   MAKE_OBJ(long long)
   Long() : value(345) {}
};

struct Int : public Object
{
   MAKE_OBJ(long)
   Int() : value(3) {}
};

int main()
{
    Object a;
    Long b;
    Int c;
    std::cout<<a.toString()<<std::endl; // BadObj
    std::cout<<b.toString()<<std::endl; // 345
    std::cout<<c.toString()<<std::endl; // 3
    return 0;
}

очевидно, трюк в классы std:: ostringstream, которые принимают любой тип параметра (long long, long и т. д...). Поскольку это стандартная практика C++, это не должно иметь значения.