When might it make sense to use inheritance when templates(compile-time polymorphism) is enough?

K

K. Frank

Hello Group!

Prior to templates, if you wanted to reuse code across
different types, you would use inheritance / polymorphism
where your different types would derive from a common
base type that offered the capabilities needed by your
reusable code.

Now (with templates) if you know at compile time which
objects are of which concrete types you can use templates.
(If the concrete type of an object is only determined at
run time, you still need inheritance.)

This is kind of a soft question, but I'm wondering whether
there are situations where inheritance would still be
preferable, even when concrete types are known at compile
time, and a template solution could have been used.

Assume for this question that the code is special purpose,
so we're not trying to write some general, open-ended
library. That is, the code will be reused across a
smallish number of different types that are all being
designed together, and will not be extended to new types
in the future.

Also assume that efficiency isn't a concern, so that we
don't care about the cost of vtables or the cost (size)
of duplicated template code.

To illustrate my question, below is a simple, do-nothing
example that uses both inheritance and templates for
compile-time polymorphism. printPrintable is an
inheritance-based polymorphic function, while
printHasPrintMe is a generic template function.


Thanks for any thoughts and wisdom.


K. Frank


==========

#include <iostream>

class Printable {
public:
virtual void printMePoly() const = 0;
};

class A : public Printable {
public:
void printMePoly() const { std::cout << "aValue_ = " << aValue_ << std::endl; }
int aValue_ = 13;
};

class B {
public:
void printMeGen() const { std::cout << "bValue_ = " << bValue_ << std::endl; }
int bValue_ = 17;
};

void printPrintable (const Printable& p) {
p.printMePoly();
}

template<typename T> void printHasPrintMe (const T& p) {
p.printMeGen();
}

int main (int argc, char *argv[]) {
A a;
B b;
printPrintable (a);
printHasPrintMe (b);
}

==========
 
V

Victor Bazarov

Prior to templates, if you wanted to reuse code across
different types, you would use inheritance / polymorphism
where your different types would derive from a common
base type that offered the capabilities needed by your
reusable code.

Now (with templates) if you know at compile time which
objects are of which concrete types you can use templates.
(If the concrete type of an object is only determined at
run time, you still need inheritance.)

This is kind of a soft question, but I'm wondering whether
there are situations where inheritance would still be
preferable, even when concrete types are known at compile
time, and a template solution could have been used.

When you use "duck typing", you only care about the "duck" part of your
type, and never about anything else. Granted, the class (the "duck")
should take care of itself, of course. When you use inheritance, the
bigger part is pulled in. From how I feel about it, inheritance is a
bit more flexible and a bit more powerful than "compile time
polymorphism" achievable through "duck typing".
Assume for this question that the code is special purpose,
so we're not trying to write some general, open-ended
library. That is, the code will be reused across a
smallish number of different types that are all being
designed together, and will not be extended to new types
in the future.

That's a slippery slope, isn't it? Remember, in the 1970s, when 640
Kbytes was considered "enough for anybody" using a personal computer?
The addressing scheme in the microprocessor's "real" mode was limited to
20 bits of addressable space because they *assumed* that there will be a
limited use to the whole thing...

So, don't mean to snap at you. Inheritance has its place. Public
inheritance implements Liskov's substitution principle. Private
inheritance is a way to pull in all the functionality ("implemented in
terms of") without having to recompile everything.

"Duck typing" on the other hand is the freedom from the limitations
imposed by inheritance. Use it whenever you feel confined or obstructed
by too much dependence between types. Now, flexibility and freedom are
not the same thing, so don't try to catch me because I just said that
"inheritance is more flexible". "Duck typing" is not flexible, it's loose.
Also assume that efficiency isn't a concern, so that we
don't care about the cost of vtables or the cost (size)
of duplicated template code.

Efficiency isn't a concern unless (or until) it's _confirmed_ to suffer
and then one should use proper tools to discover the cause of
inefficiency. One should never *assume* that the use of vtables or
inheritance is somehow inferior in terms of efficiency to templates.
Both methods are ways to *model* your environment, to *represent* your
design. Efficiency gained by direct calls (instead of polymorphic ones)
is usually borrowed from something else, like clarity or maintainability...

Take my comments with a grain of salt. I've not exercised my skills in
C++ design in some months...

V
 
J

Jorgen Grahn

On 8/17/2013 5:37 PM, K. Frank wrote: ....

That's a slippery slope, isn't it? Remember, in the 1970s, when 640
Kbytes was considered "enough for anybody" using a personal computer?
The addressing scheme in the microprocessor's "real" mode was limited to
20 bits of addressable space because they *assumed* that there will be a
limited use to the whole thing...

I find it very sensible not to try to create a generally useful
library for everything you do. IMO, in our business it's more common
for people to plan /too much/ for a hypothetical future than too
little. That's where the "you ain't gonna need it" mantra comes from.

My personal rule is to do as little work as possible ... unless one of
these apply:

- the general solution is actually a lot easier to explain
and think about

- it's easy to by mistake step outside the boundaries, and when
you do the compiler cannot tell you.

- the decision is fundamental and hard to undo (e.g. that 640K MS-DOS
thing, or basing everything on global variables)

I don't mind having to rework a class or two when new requirements
arrive. That will happen anyway; this way at least I don't have to
maintain code which isn't used.

/Jorgen
 
K

K. Frank

Hi Victor and Paavo!

in


There are plenty of reasons to use inheritance for some kind of
functionality even if run-time polymorphism is not needed (assuming
performance issues are not a concern either way).

- using inheritance imposes more stringent restrictions over the
involved classes than templates, so there is more formal control and
structure

I think in terms of my question, this is probably the
key benefit of inheritance. And I think it contrasts
well with Victor's comment about the template approach
(what he called "duck typing") about which he said:
"it's loose."

So in the template case, we avoid the "nuisance" of
having to derive from a specific base class, while
in the inheritance case, we have the benefit of a
well-defined interface.

I would describe it as follows: Absent concepts, you
can't use templates to specify the required interface
in an organized way -- the requirements are given
implicitly by what the template's implementation does
with the type with which it's instantiated.

For example, in my template example:

template<typename T> void printHasPrintMe (const T& p) {
p.printMeGen();
}

it's only in the implementation that we see that T is
required to have a printMeGen member function.

In my inheritance example, I used an abstract base class to
define a pure interface. This specifies that the derived
type must implement the printMePoly member function:

class Printable {
public:
virtual void printMePoly() const = 0;
};

and we do not need to look at the actual implementation
of the (non-template) printPrintable function:

void printPrintable (const Printable& p) {
p.printMePoly();
}

to see that the printMePoly function is required; we know
this already from the Printable interface.

I wonder how far adding concepts to c++ would go in
giving the template approach these same benefits.


Thanks.


K. Frank
 
V

Victor Bazarov

Hi Victor and Paavo!
[..]
I would describe it as follows: Absent concepts, you
can't use templates to specify the required interface
in an organized way -- the requirements are given
implicitly by what the template's implementation does
with the type with which it's instantiated.
[..]

If you haven't already, take a look at "Modern C++ Design" by Andrei
Alexandrescu. It's actually full of examples that effectively (and
quite so) marries the two approaches: inheritance and templates. Some
might say that instead of opposing those two techniques, it makes them
work together.

V
 
J

James Kanze

Prior to templates, if you wanted to reuse code across
different types, you would use inheritance / polymorphism
where your different types would derive from a common
base type that offered the capabilities needed by your
reusable code.
Now (with templates) if you know at compile time which
objects are of which concrete types you can use templates.
(If the concrete type of an object is only determined at
run time, you still need inheritance.)
This is kind of a soft question, but I'm wondering whether
there are situations where inheritance would still be
preferable, even when concrete types are known at compile
time, and a template solution could have been used.

Are there really that many cases where both are appropriate.
Most of the time, only one will do the job, and in the few
remaining cases, one is usually significantly better than the
other. They do different things. Radically different, in
fact. Inheritance allows different implementations of the same
interface; templates allow the same implementation for different
interfaces.
Assume for this question that the code is special purpose,
so we're not trying to write some general, open-ended
library. That is, the code will be reused across a
smallish number of different types that are all being
designed together, and will not be extended to new types
in the future.
Also assume that efficiency isn't a concern, so that we
don't care about the cost of vtables or the cost (size)
of duplicated template code.
To illustrate my question, below is a simple, do-nothing
example that uses both inheritance and templates for
compile-time polymorphism. printPrintable is an
inheritance-based polymorphic function, while
printHasPrintMe is a generic template function.

If you're requiring a printMe function in any class you call it
on, you might as well go with inheritance, for the improved
flexibility, and the (almost certainly) better error messages if
you try to call it on a class which doesn't support it. The
template is just extra baggage.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,763
Messages
2,569,560
Members
45,035
Latest member
HoTaKeDai

Latest Threads

Top