Can I change the return type when overriding a virtual function in C ++?

I am having problems redefining virtual functions, in fact we are talking about hessian (web service protocol).

it has a base class Object, and some derived classes: Long, Int, String, ..., all derived classes do not have a virtual function "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_; ... }; 

now I want to add a function, say, toString, which can convert Object to a string:

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

if I can change the value function to virtual, I only need to write the toString function in Object, otherwise I need to write the toString virtual function and override these functions in all derived classes.

eg

  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()); } }; 

but I cannot write a virtual value function because the return value cannot be redefined.

Is there a good solution to this problem?

thanks

+7
c ++ function override virtual
source share
6 answers

No, you cannot write toString in Object using the virtual function "value" and override the type of the return value. However, you can write a virtual toString and with the pattern programming trick do almost the same thing.

 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; 
+2
source share

can I change the type of the return value when overriding a virtual function in C ++?

Only in very limited mode, in that the (raw) pointer or reference return type can be covariant.

is there a good solution to this problem?

Well, there are two pretty good solutions and one weak solution.

I offer you a little bad solution. One of the reasons I give this is because it is easy to understand or at least quite easy to “copy and modify”, even if it is not entirely clear. Another reason is that one of the good solutions requires some broad general support equipment, which does not have room for discussion here, and another good solution (which, in my opinion, is best in all respects), has the form that at least when I presented such a solution, I automatically received a disk with downvotes and only this, here, on SO. I assume that the price of paying for diversity is here, which variety is a very good thing :-) But, unfortunately, this means that it makes no sense to offer real good things, then I would refuse a negative reputation.

In any case, code based on dominance in virtual inheritance; it is about the same as inheriting an interface implementation in Java or 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; } 

If your classes are Long and Int , etc. look very much the same as they seem, consider defining only one class template, or perhaps inherit from the specializations of such a template (this can also help avoid errors, as this reduces redundancy).

EDIT . Now I see that you accepted the answer, which is essentially my last suggestion on templates. This means that I answered the question posed (a solution for different, different classes), while you had something less general. Oh good.

Cheers and hth.,

+7
source share

Unfortunately, you cannot overload functions in C ++ by the return value. What you could do if you have a suitable some_convert_function for all the types you need, it would be to create a free template function that looks something like this:

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

You cannot override a function with a different return type; the closest you can find is to hide the function in the parent object with another in the derived class. But this is not what you want, because the two will be different functions, completely unrelated.

You were right in assuming that you needed to create a new toString function in each derived class - about what polymorphism is.

0
source share

I don’t think you will get better. Although it is possible in some cases to change the return type of a virtual function, consider the following: how is your function used? If it is virtual, the changes are that users will use the base class. So they don’t notice what the actual type of your class is, and therefore they won’t know what type to expect. So:

  • Return the base class type.
  • Return functions that give you the correct type (i.e. virtual std::string getStringValue() , which gives you a string, if applicable).
  • Use templates if the type is known to the user.
0
source share

Regarding the comment by @MerickOWA, here is another solution that does not require any additional template mechanism.

Since you intended to have a virtual "value ()" method that you need to implement in all classes, I expanded this idea (as a rule, in such structures you have many similar "basic" methods, so I used a macro to write them for me, it is not required, it is simply faster and less error prone.

 #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; } 

Obviously, the trick is in the std :: ostringstream classes, which take any type of parameter (long long, long, etc.). Since this is standard C ++ practice, it does not matter.

0
source share

All Articles