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

Discussion in 'C++' started by K. Frank, Aug 17, 2013.

  1. K. Frank

    K. Frank Guest

    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);
    }

    ==========
    K. Frank, Aug 17, 2013
    #1
    1. Advertising

  2. Re: When might it make sense to use inheritance when templates (compile-timepolymorphism) is enough?

    On 8/17/2013 5:37 PM, K. Frank wrote:
    > 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
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Aug 17, 2013
    #2
    1. Advertising

  3. K. Frank

    Jorgen Grahn Guest

    On Sat, 2013-08-17, Victor Bazarov wrote:
    > On 8/17/2013 5:37 PM, K. Frank wrote:

    ....
    >> 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...


    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

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Aug 19, 2013
    #3
  4. K. Frank

    K. Frank Guest

    Hi Victor and Paavo!

    On Sunday, August 18, 2013 5:54:50 PM UTC-4, Paavo Helde wrote:
    > "K. Frank" wrote in
    > news::
    >
    > > 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.

    >
    > 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
    K. Frank, Aug 19, 2013
    #4
  5. Re: When might it make sense to use inheritance when templates (compile-timepolymorphism) is enough?

    On 8/19/2013 10:45 AM, K. Frank wrote:
    > 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
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Aug 19, 2013
    #5
  6. K. Frank

    James Kanze Guest

    On Saturday, 17 August 2013 22:37:17 UTC+1, K. Frank wrote:
    > 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.

    --
    James
    James Kanze, Aug 19, 2013
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. TR
    Replies:
    1
    Views:
    384
    John Saunders
    Dec 14, 2004
  2. sapropel
    Replies:
    1
    Views:
    583
    Victor Bazarov
    May 14, 2004
  3. Samee Zahur
    Replies:
    1
    Views:
    4,143
    Jerry Coffin
    Apr 17, 2005
  4. Samee Zahur
    Replies:
    7
    Views:
    4,399
    =?ISO-8859-1?Q?Tr0n=B2?=
    Apr 20, 2005
  5. Lou Pecora
    Replies:
    8
    Views:
    321
Loading...

Share This Page