views:

464

answers:

5

We have a class hierarchy which looks something like this:

class base
{
};

class derived1 : protected base
{
private:
    float m_price;
    int m_quantity;
    float m_value;
public:
//  float calculateValue();
};

class derived2 : protected base
{
private:
    double m_price;
    long m_quantity;
    double m_value;
public:
//  double calculateValue();
};

Now we need to write a function which computes the value by multiplying the price and quantity. The aim is to make it as simple as possible to add new classes in the future. As you may be aware, this is not straightforward because the data types of these fields are different for different classes. In effect, we have these functions to do the same thing conceptually, but in programming terms they are different operations.

To minimise the amount of cut and paste required, the solution I can think of so far is to use template functions:

template <class A, B, C>
A calculate_value(B price, C quantity)
{
    A result;
    // Some code to do the multiplication, not sure if template specialisation is needed
    return result;
};

class derived1 : protected base
{
private:
    float m_price;
    int m_quantity;
    float m_value;
public:
    float calculateValue()
    {
       calculate_value < float, float, int > (m_price, m_quantity);
    }
};

It does its job all right, but this will mean that I have to define every single member function in each class. For example, I will need another lot of these template functions if I want to have a function called getValue, say.

The data types of the class members are known when the class is defined, so having to put them in the function definitions again seem to be a duplication. Is there a possible way to avoid all this template business in the function definitions?

Thank you.

Andy

PS I have seen the following question, but the issue in that question is slightly different: http://stackoverflow.com/questions/930932/returning-different-data-type-depending-on-the-data-c

A: 

The OO solution would be to create a class of the return type. You can then subclass this return type for specialized return types.

In any case, using floating point math for money will get you in trouble.

laalto
I agree that usinig floating point mathematics for repeated multiplication/division is not a good idea. This is just done as an illustrative example. In hindsight I could have chosen another operation.
Andy
+5  A: 

While I can't say I like the idea of having multiple derived classes with a function that returns different types, there is a way to do this.

template
class base<typename value_type>
{
public:
    value_type calculateValue();
};
class derived1 : protected base<float>
{
private:
    float m_price;
    int m_quantity;
    float m_value;
};
class derived2 : protected base<double>
{
private:
    double m_price;
    long m_quantity;
    double m_value;
};

This lets you vary the value_type in the derived class, but declare all your common functions in the base (like you ought to do). This is similar to the approach used in the STL for maps and such.

Harper Shelby
A: 

Would something like this do?

template <typename A,typename B,typename C>
class base{
protected:
 A m_price;
 B m_quantity;
 C m_value;
public:
 C calculateValue(){
  m_value = m_quantity * m_price;
  return m_value;
 }
};

class derived1 : public base<int,float,int>{
};

class derived2 : public base<long,double,long>{
};
Paolo Tedesco
+1  A: 

Use the Curiously Recurring Template Pattern (CRTP):

template <typename DERIVED>
class base {
protected:
    typename DERIVED::value_type calculateValue() {
        DERIVED *self = static_cast<DERIVED *>(this);
        return self->m_price * self->m_quantity;
    }
};

class derived1 : protected base<derived1> {
public:
    typedef float value_type;
    float m_price;
    int m_quantity;
};

class derived2 : protected base<derived2> {
public:
    typedef double value_type;
    double m_price;
    long m_quantity;
};

Note that I've had to make m_price and m_quantity public in order that the base class can access them. You probably don't want to do that, so instead you will need either to add public accessors (or use the ones which are there already, if any), or else make them protected members of the base class (of types specified by typedefs in the derived class), or else have the derived class declare the base class a friend.

If you want a public getValue member function, you can add it to the base class and make the inheritance public.

Steve Jessop
A: 
Craig W. Wright