C++/c/c++ Overloading problem
Expert: Ralph McArdell - 8/30/2006
QuestionHi,
i am stuck on a Alocated_variable_mem_stack class.
see source here:
http://people.zeelandnet.nl/marco01974/cpp.zip
// example code (to explain what goes wrong)
template <class Ptype>
class Base
{
Ptype * m_data;
};
class Variant
{
Base<int> m_Pint; // = * var
Base<char> m_Pchar; // = * var
public;
void set(int x){} // works
void set(char * x}{} // works
// get (doesn`t work)
int get(){return *m_Pint.data;}
char * int get(){return *m_Pchar.data;}
---------^^^^^^^^^^^-------------
// error: function get already defined.. :=(
};
This is some psuedo code.. the source is more complex and has 1 error (function GET already defineD :-( )
download my src on
http://people.zeelandnet.nl/marco01974/cpp.zip
if it would work, i could make all aplication variables resist on a class object ++ being dynamicly freed if they leave c++ scope.
if i can get GET FUNCTON overloaded it would be perfect for me.
Thanx.
Answer[note: I have not got the time to read through your full code so have only looked at what you posted in your question - sorry]
The reason you are having problems is that you cannot overload function names on return type in C++. Overloaded functions have to differ in either the number of parameters or the type of at least 1 parameter.
The reason (from "The C++ Programming language 3rd edition" by Bjarne Stroustrup section 7.4.1) is to keep the resolution of which overloaded function to call context independent (i.e. looking at just the call to a function is enough to determine which overload is called). He gives the following example:
float sqrt( float );
double sqrt( double );
void fn( double da, float fa )
{
float f = sqrt( da ); // calls sqrt(double)
double d = sqrt( da ); // calls sqrt(double)
f = sqrt( fa ); // calls sqrt(float)
d = sqrt( fa ); // calls sqrt(float)
}
The comments show which overload of sqrt is called, and are easily worked out just by looking at the type of the argument passed to sqrt, and only the call to sqrt needs to be considered (sqrt(da) or sqrt(fa) in the example). If the return type were taken into account then much more of the expression needs to be taken into account in order to determine which overload to call, as what the return type of the function is needs to be determined (for example d = sqrt( fa )). Now the example has simple cases, how about these cases:
d = sqrt( sqrt(fa) + sqrt(da) );
f = sqrt( sqrt(da) + sqrt(fa) );
Then of course it is legal and common not to store the return value at all in C and C++, as below:
sqrt( da );
In such cases overload resolution would be impossible if the return type needed to be taken into account as there is none (i.e. it could be void or anything else).
So I think that with all this extra complexity to allow overloading on return types peoples brains imploded and they said enough is enough - we will not allow it!
So what options do you have?
Well the most obvious is to give your get functions different names, maybe getInt, getChar, getCStr or some such.
Another, which in this case may be acceptable as you seem to be implementing some sort of variant type would be to provide type conversion operators for each type you variant can represent:
operator int() const { return *m_Pint.data; }
operator char() const { return *m_Pchar.data; }
Note that I specify operator char not char* this is because your class stores pointers to int and char not pointers to int and char*.
You should also be aware of potential ambiguities where a value can be converted either by a call to a single parameter constructor or to a conversions operator and problems caused due to the sort of conversion usually applied implicitly for you, such as char to int conversions. Assigning a char variant to an int will probably cause the operator int to be called even if the variant contains a char. In such cases explicit conversion is required using a cast. For more on conversion operators see for example "The C++ Programming language 3rd edition" section 11.4.
You seem to be storing multiple pointers, is this really necessary?
In the past I have used a similar but simpler approach to this sort of problem. Again I defined a base type which acts as a common base class and defined an empty virtual destructor, the reason for which I describe below:
// Header
class VariantBase
{
public:
virtual ~VariantBase();
};
// Implementation
VariantBase::~VariantBase()
{
}
Unlike your base type however this is not a template type, and pointers to which can be stored in your stack or used in other places where you require a variant type. The variant type itself is a template, which can store a single value of any type T, and is derived from VariantBase:
// Header
template <class T>
class Variant : public VariantBase
{
public:
explicit Variant( T value ) // Note: value is passed by value
: mValue( value )
{
}
virtual ~Variant();
// Variant<T> is convertible to T (T must be copyable)
operator T() {
return mValue;
}
private:
T mValue; // Copy of value passed in constructor
};
// Header (assuming inclusion model for templates)
template <class T>
Variant<T>::~Variant()
{
}
You use these classes like so (assuming an STL like stack type):
stack s<VariantBase*>;
s.push( new Variant<int>(10) );
s.push( new Variant<char>('x') );
To extract items safely I used dynamic_cast:
Variant<char>* v = dynamic_cast<Variant<char> >( s.top() );
if ( ! v )
{
// handle wrong element type error
}
char c = v;
These details can of course can be wrapped up and made generic in a template function.
The reason for VariantBase having a virtual destructor is two fold: first the usual reason that it is a base class, and so needs a virtual destructor to ensure derived type instances referenced through base pointers (or references) are destroyed correctly. Secondly, dynamic_cast relies on RTTI (Run Time Type Information), which only works for types having virtual members (this is because RTTI implementations typically use the vtable which is only present for classes with virtual functions). Note: you may have to use a compiler switch or option to turn on RTTI.
Now the above approach may not be 100% suitable for what you wish, but I hope it at least gives so some ideas for alternatives. Specifically I would have thought in this case, where you might not know in advance the type of the values popped off the stack, that a type discrimination value would be of use (such as an enum having enumerated values such as v_int, v_char etc.), it depends on what you are doing with all this variant data!