-2

I've been told that generic programming (in C++) can be used instead of polymorpshism, but I've never understood how/why. I've always used generic programming for cases when my class can function with multiple types, and I use polymorpshim when I want different behavior from derived classes (i.e. the standard polymophic behavior). I just don't the connection between these two (seemingly) different concepts. I was thinking that you could use a polymorphic function to exhibit different behaviors based on the Type of data which is inputted by the user, but I'm not even sure if that is possible.

How can generic programming be used instead of polymophism? What are the advantages of doing so? Please provide an example if possible.

Ryan J. Shrott
  • 592
  • 1
  • 8
  • 26

3 Answers3

1

Derived classes are multiple types. Each derived class is a type, so having different derived classes means having multiple types.

Generics are typically superior to runtime polymorphism because the type information isn't lost- the compiler still has access so it can enable a greater degree of type checking. A simple example of how runtime polymorphism epically fails at this is non-generic collection types.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • I still don't see how a generic class can emulate polymorphic behavior. In particular, how can the "generic" function exhibit different behaviors based on the Type? – Ryan J. Shrott Aug 10 '15 at 21:26
  • The same way as runtime polymorphism- it can simply call functions on the type, and that type can implement them however it wishes. – Puppy Aug 10 '15 at 21:34
1

In C++, there are two types of polymorphism: compile time and run time.

Compile time polymorphism

Compile time polymorphism is accomplished by using templates. Relying on this form of polymorphism is also known as generic programming.

Many functions in the standard library, such as std::sort, std::find, depend on compile time polymorphism. As long as the parameters used to instantiate the functions support the required functionality, those functions work just fine.

Simple example:

template <typename T>
T const& min(T const& lhs, T const& rhs)
{
   return (lhs < rhs) ? lhs : rhs;
}

The implementation of min will be able to return the min of two values regardless of their type as long as the operation lhs < rhs is supported by the type.

Run time polymorphism

Run time polymorphism is accomplished by using virtual functions. This is the more widely known form of polymorphism.

Functions that rely on run time polymorphism work with a base class pointer or reference. They make calls to the virtual functions that are declared at the base class level. The calls are dispatched to derived classes based on run time information associated with the object.

Simple example:

struct Shape
{
   virtual double getArea() const = 0;
}

void foo(Shape const& s)
{
    double area = s.getArea();
    // Use area
}

foo will worth any valid Shape object regardless of the concrete type.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

You need to be clear on what you want to achieve; how should be your second question. You can use polymorphism to avoid writing new code, or you can use it to avoid changing old code.

If all you want is a class or a function to behave differently for different types, then static polymorphism will do the trick. Sometimes plain function overload will be enough; sometimes you will have to use templates; occasionally you will have to specialise your templates for edge cases. This mostly tackles the issue of writing new, redundant code: why write IntVectors and StringVectors if a compiler can write them for you.

You should derive classes only when you want to refer to them through a base class pointer. Dynamic polymorphism allows you to reuse existing code without modifying it: if you have a function that displays widgets on screen, you don't need to rewrite it when you introduce a new type of widget.

Now, if you can combine static and dynamic polymorphism, you've got the power. A random example that comes to mind is a spreadsheet, but it might not be the best:

class BaseCell {
    // ...
public:
    virtual void draw() = 0;
};

template<class CellFormat>
class Cell : public BaseCell {
public:
    virtual void draw(); // uses something supplied by CellFormat
};

// somewhere else
vector<shared_ptr<BaseCell>> cells {
    shared_ptr<BaseCell>{new Cell<IntFormat>()},
    shared_ptr<BaseCell>{new Cell<CurrencyFormat>()}
};

for (auto cell: cells) {
    cell->draw();
}

In this way you can write future proof functions that use BaseCell provided interface, reducing changes to the existing codebase, and generate different classes for new supported formats, reducing new code that has to be written.

To sum up: runtime and static polymorphism are not substitutes, but they are complementing techniques that share the idea of uniform interface to different behaviours.

Maksim Solovjov
  • 3,147
  • 18
  • 28