When people talk about polymorphism in C++ they usually mean the thing of using a derived class through the base class pointer or reference, which is called subtype polymorphism. But they often forget that there are all kinds of other polymorphisms in C++, such as parametric polymorphism, ad-hoc polymorphism and coercion polymorphism.

These polymorphisms also go by different names in C++,

  • Subtype polymorphism is also known as runtime polymorphism.
  • Parametric polymorphism is also known as compile-time polymorphism.
  • Ad-hoc polymorphism is also known as overloading.
  • Coercion is also known as (implicit or explicit) casting.

In this article I'll illustrate all the polymorphisms through examples in C++ language and also give insight on why they have various other names.

Subtype Polymorphism (Runtime Polymorphism)

Subtype polymorphism is what everyone understands when they say "polymorphism" in C++. It's the ability to use derived classes through base class pointers and references.

Here is an example. Suppose you have various cats like these felines,

Polymorphic Cats
Polymorphic cats on a mat by James Halliday.

Since they are all of Felidae biological family, and they all should be able to meow, they can be represented as classes inheriting from Felid base class and overriding the meow pure virtual function,

// file cats.h

class Felid {
public:
 virtual void meow() = 0;
};

class Cat : public Felid {
public:
 void meow() { std::cout << "Meowing like a regular cat! meow!\n"; }
};

class Tiger : public Felid {
public:
 void meow() { std::cout << "Meowing like a tiger! MREOWWW!\n"; }
};

class Ocelot : public Felid {
public:
 void meow() { std::cout << "Meowing like an ocelot! mews!\n"; }
};

Now the main program can use Cat, Tiger and Ocelot interchangeably through Felid (base class) pointer,

#include <iostream>
#include "cats.h"

void do_meowing(Felid *cat) {
 cat->meow();
}

int main() {
 Cat cat;
 Tiger tiger;
 Ocelot ocelot;

 do_meowing(&cat);
 do_meowing(&tiger);
 do_meowing(&ocelot);
}

Here the main program passes pointers to cat, tiger and ocelot to do_meowing function that expects a pointer to Felid. Since they are all Felids, the program calls the right meow function for each felid and the output is:

Meowing like a regular cat! meow!
Meowing like a tiger! MREOWWW!
Meowing like an ocelot! mews!

Subtype polymorphism is also called runtime polymorphism for a good reason. The resolution of polymorphic function calls happens at runtime through an indirection via the virtual table. Another way of explaining this is that compiler does not locate the address of the function to be called at compile-time, instead when the program is run, the function is called by dereferencing the right pointer in the virtual table.

In type theory it's also known as inclusion polymorphism.

Parametric Polymorphism (Compile-Time Polymorphism)

Parametric polymorphism provides a means to execute the same code for any type. In C++ parametric polymorphism is implemented via templates.

One of the simplest examples is a generic max function that finds maximum of two of its arguments,

#include <iostream>
#include <string>

template <class T>
T max(T a, T b) {
 return a > b ? a : b;
}

int main() {
 std::cout << ::max(9, 5) << std::endl;     // 9

 std::string foo("foo"), bar("bar");
 std::cout << ::max(foo, bar) << std::endl; // "foo"
}

Here the max function is polymorphic on type T. Note, however, that it doesn't work on pointer types because comparing pointers compares the memory locations and not the contents. To get it working for pointers you'd have to specialize the template for pointer types and that would no longer be parametric polymorphism but would be ad-hoc polymorphism.

Since parametric polymorphism happens at compile time, it's also called compile-time polymorphism.

Ad-hoc Polymorphism (Overloading)

Ad-hoc polymorphism allows functions with the same name act differently for each type. For example, given two ints and the + operator, it adds them together. Given two std::strings it concatenates them together. This is called overloading.

Here is a concrete example that implements function add for ints and strings,

#include <iostream>
#include <string>

int add(int a, int b) {
 return a + b;
}

std::string add(const char *a, const char *b) {
 std::string result(a);
 result += b;
 return result;
}

int main() {
 std::cout << add(5, 9) << std::endl;
 std::cout << add("hello ", "world") << std::endl;
}

Ad-hoc polymorphism also appears in C++ if you specialize templates. Returning to the previous example about max function, here is how you'd write a max for two char *,

template <>
const char *max(const char *a, const char *b) {
 return strcmp(a, b) > 0 ? a : b;
}

Now you can call ::max("foo", "bar") to find maximum of strings "foo" and "bar".

Coercion Polymorphism (Casting)

Coercion happens when an object or a primitive is cast into another object type or primitive type. For example,

float b = 6; // int gets promoted (cast) to float implicitly
int a = 9.99 // float gets demoted to int implicitly

Explicit casting happens when you use C's type-casting expressions, such as (unsigned int *) or (int) or C++'s static_cast, const_cast, reinterpret_cast, or dynamic_cast.

Coercion also happens if the constructor of a class isn't explicit, for example,

#include <iostream>

class A {
 int foo;
public:
 A(int ffoo) : foo(ffoo) {}
 void giggidy() { std::cout << foo << std::endl; }
};

void moo(A a) {
 a.giggidy();
}

int main() {
 moo(55);     // prints 55
}

If you made the constructor of A explicit, that would no longer be possible. It's always a good idea to make your constructors explicit to avoid accidental conversions.

Also if a class defines conversion operator for type T, then it can be used anywhere where type T is expected.

For example,

class CrazyInt {
 int v;
public:
 CrazyInt(int i) : v(i) {}
 operator int() const { return v; } // conversion from CrazyInt to int
};

The CrazyInt defines a conversion operator to type int. Now if we had a function, let's say, print_int that took int as an argument, we could also pass it an object of type CrazyInt,

#include <iostream>

void print_int(int a) {
 std::cout << a << std::endl;
}

int main() {
 CrazyInt b = 55;
 print_int(999);    // prints 999
 print_int(b);      // prints 55
}

Subtype polymorphism that I discussed earlier is actually also coercion polymorphism because the derived class gets converted into base class type.

Have Fun!

Have fun with all the new knowledge about polymorphism!

Comments

serenity Permalink
June 18, 2010, 10:47

"If you made the constructor of A explicit, that would no longer be possible. It's always a good idea to make your constructors explicit to avoid accidental conversions."

Hm. I understood everything else clearly, but this has me stumped. Isn't "55" (without quotes, obviously) an int, which gets passed to a constructor expecting an int?

June 18, 2010, 10:52

Oops, you're right! I am modifying the example.

gopi Permalink
October 30, 2012, 17:43

very useful post..

Josh Permalink
June 18, 2010, 11:11

Nice article but, when don't use virtual method the invocation resolution use the data type declaration of this variable in other words hiding.

fulano Permalink
June 18, 2010, 11:26

The way I see it is that there are two orthogonal axes here: polymorphism kind (ad-hoc vs parametric), and mechanism kind (dynamic vs static).

In parametric polymorphism the code works with an unlimited set of (variable, parametrized) types, and the behaviour is essentially identical for all of those types.

Contrast this with ad-hoc polymorphism, where there is a limited, enumerated set of types which are usable in the context, and the behaviour of the code varies as those types change.

On the other axis, dynamic vs static refers to whether the mechanisms which implement the polymorphism are run-time or compile-times ones.

In C++ this leads us to the following classifications:

Subclassing: dynamic, ad-hoc (NB it's only dynamic in the case of virtual functions)

Templates: static, parametric

Overloading: static, ad-hoc

(Can't think of a built-in, dynamic, parametric polymorphism in C++.)

Introduce template specialization, and you start mixing in ad-hoc features into the parametric polymorphism.

I don't really think that it is meaningful to think of casting as a kind of polymorphism.

June 18, 2010, 17:10

That's a cool way of viewing it. I hadn't thought about it before. Thanks for sharing this view.

Julien Permalink
June 19, 2010, 00:45

Casting any pointer to void* could be considered a limited form of built-in, dynamic, parametric polymorphism.

By stretching things a bit further, you could even see const_cast and reinterpret_cast as falling into the same category.

fulano Permalink
June 19, 2010, 06:39

Agreed. However,

a) inheritance and virtual functions

b) function overloading

c) templates

are all built-in mechanisms for the *creation* of new polymorphic components in a C++ program. These casts simply *are* polymorphic components: you have no means of extending the set.

You could argue that you can use them (especially the void*) to create new polymorphic components, but then *I* wouldn't call it a built-in mechanism any more (it becomes a Turing-trivial argument).

Still, I appreciate the observation. I hadn't thought of the casts in this light before.

June 18, 2010, 14:13

memo - I know what you mean.

Despite its flaws, a book that is absolutely wonderful on the amazing things that parametric polymorphism is capable of is Alexandrescu's "Modern C++ Design". It's short but deep, and will leave you astounded.

June 18, 2010, 17:09

I deleted memo's comment because it was spam. For the reference, he said he didn't like C++.

Alain Bo Permalink
June 18, 2010, 16:27

Are you making up these new terms?
Polymorphism means the ability to take multiple forms and I guess that in a way you could apply the term as you do but since they are different things, why would you want to do so.

If you overload the term polymorphism to mean additional things, you're only confusing the different techniques IMO. Type casting is type casting, generic programming is generic programming and overloading is overloading. All these techniques have a name, why invent new ones?

Zach T Permalink
June 18, 2010, 16:49

Alain Bo:

Because there's a mathematical theory that underpins programming languages, and the terms like "overloading" "generic programming" and "casting" are terms invented on top of *that* language to simplify matters for mere mortals. In that language, they are the same. Generic programming *is* compile-time polymorphism. This is a well understood fact. That people refer to it by a more friendly name is just a matter of convenience.

Alain Bo Permalink
June 19, 2010, 13:27

Zack,

Thanks for the info. Maybe I don't hang around the right corners on the Internet. I arrived at this blog from the reddit C++ section and the title of the article is "... Polymorphism in C++." I expected something about C++. What I got instead is that it's possible to talk about different techniques in the same article because these techniques can also be named "something" polymorphism.

I learned something new so it's not all bad.

June 18, 2010, 17:09

I haven't invented a single term.

PepeMandioca Permalink
June 18, 2010, 17:43

He's not inventing stuff, read Luca Cardelli's 1985 paper "On understanding types, data abstraction, and polymorphism" (http://lucacardelli.name/Papers/OnUnderstanding.pdf)

Dan Permalink
June 18, 2010, 17:39

Also don't forget (hat tip to Scott Meyers) about "link time" polymorphism.

If I call the free function foo(int) -- which foo am I calling? I can decide that at link time.

I've used this mostly for testing, but it's surprisingly useful.

general exception Permalink
June 19, 2010, 16:55

and how about this one? http://www.youtube.com/watch?v=26pJ4rU5cfE&feature=PlayList&p=D132A5CA15D41FC0&playnext_from=PL&playnext=1&index=6

July 01, 2010, 20:05

Nice post. Do you have any pointers with more info ?
Thanks.

July 08, 2010, 08:06

Hi Peteris
congrats dude keep up the good work.
Rock the world. All the best

November 22, 2010, 18:05

Hey, thanks for posting all of this source, very helpful! :-)

diana Permalink
February 02, 2011, 19:21

nice information ! really helpful :D .anw do you know about another type of polymorphism like: dynamic polymorphism, static polymorphism, universal polymorphism ?thanks for ur help :D

December 19, 2011, 05:25

thanks of posting of all code

Gangaraju Raj Permalink
June 11, 2012, 12:14

really nice info!.. everything regarding Polymorphism is there.. can understand easily.. Thanks for the info

vishal Permalink
August 21, 2012, 17:07

why runtime polymorphism usefull in c++?

fondo Permalink
June 06, 2013, 12:29

hi i was given an assignment to write about different kinds of polymorphism. would you please tell me how many they are?

Martin Chetlen Permalink
August 04, 2013, 22:28

This may fall under parametric polymorphism. The use of function pointers and function objects as function parameters is also polymorphic behavior in C++. In this case, different function pointers/objects can be passed into a single function as parameters resulting in different function behaviors.

August 17, 2013, 16:22

Definitely a great design blog. This can be an individual and compared with your post. Keep it up more posting like this.

Coder Permalink
October 06, 2013, 19:15

This is the most helpful and well structured information about polymorphism that I have read. Good job

macy Permalink
October 16, 2013, 07:07

Great perspective about Polymorphism in C++! See my the hermes blog to find more expert articles.

Joaquim Permalink
November 07, 2013, 13:35

hey and object Polymorphism????
can we do it?
example:

void objectname::functiname()
{
}

Peregring-lk Permalink
March 13, 2014, 17:48

A tiger saying meow would be funny.

Batty Permalink
August 05, 2014, 08:49

approved afterwards for replica chanel their accomplished adroitness and unmatchable artful appeal.

July 18, 2014, 16:13

I liked this post a lot Fotorus for PC.

July 28, 2014, 09:23

I love that, it's a great post Really
dungeon hunter 4 for pc
Baron

Leave a new comment

(why do I need your e-mail?)

(Your twitter name, if you have one. (I'm @pkrumins, btw.))

Type the word "antispam": (just to make sure you're a human)

Please preview the comment before submitting to make sure it's OK.

Advertisements