c++ standard practice: virtual interface classes vs. templates

18,307

Solution 1

You're basically right, dynamic polymorphism (inheritance, virtuals) is generally the right choice when the type should be allowed to change at runtime (for example in plugin architectures). Static polymorphism (templates) is a better choice if the type should only change at compile-time.

The only potential downsides to templates are that 1) they generally have to be defined in the headers (which means more code gets #included), and this often leads to slower compile-times.

But design-wise, I can't see any problems in using templates when possible.

Which complies more with standard c++ style?

Depends on what "standard C++ style" is. The C++ standard library uses a bit of everything. The STL uses templates for everything, the slightly older IOStreams library uses inheritance and virtual functions, and the library functions inherited from C uses neither, of course.

These days, templates are by far the most popular choice though, and I'd have to say that is the most "standard" approach.

Solution 2

Properties of classic object-oriented polymorphism:

  • objects are bound at run-time; this is more flexible, but also consumes more resources (CPU) at run-time
  • strong typing brings somewhat more type safety, but the need to dynamic_cast (and its potential to blow up into a customer's face) might easily compensate for that
  • probably more widely known and understood, but "classical" deep inheritance hierarchies seem horrendous to me

Properties of compile-time polymorphism by template:

  • compile-time binding allows more aggressive optimizations, but prevents run-time flexibility
  • duck-typing might seem more awkward, but failures are usually compile-time failures
  • can sometimes be harder to read and understand; without concepts, compiler diagnostics might sometimes become enraging

Note that there is no need to decide for either one. You can freely mix and mingle both of them (and many other idioms and paradigms). Often, this leads to very impressive (and expressive) code. (See, for example, things like type erasure.) To get an idea what's possible through clever mixing of paradigms, you might want to browse through Alexandrescu's "Modern C++ Design".

Solution 3

It's something of a false opposition. Yes, the major use of inheritance and virtual functions is in iostreams, which are very old, and are written in quite a different style to the rest of the std libraries.

But many of the "coolest" modern C++ libraries such as boost do make use of runtime polymorphism, they just use templates to make it more convenient to use.

boost::any and std::tr1::function (formerly also from boost) are fine examples.

They are both single item containers to something whose concrete type is unknown at compile-time (this is especially obvious with any, because it has its own kind of dynamic cast operator to get the value out).

Solution 4

After having shoveled a little more experience on my plate, there are some things in templates that I don't like: There are certain disadvantages that disqualify template meta programming from being a useable language:

  • readability: too many brackets, too many non language enforced(therefore misused) conventions
  • for someone undergoing the usual evolution in programming languages, templates are unreadable und incomprehensible (just look at boost bgl)
  • Sometimes it feels like someone tried to write a c++ code generator in awk.
  • compiler error messages are cl(utter)ed crap
  • too many hacks necessary (most of them remedied in c++0x) to get some basic "language" like functionality.
  • No Templates in implementation files resulting in header only libraries (which is a very two sided sword)
  • usual IDE's code completion features are not of much help with templates.
  • Doing big stuff in MPL seems "haggly", can't find another word for it. Every line of templated code produces constraints on that template type, which are enforced in a text-replacing kind of way. There are semantics inherent in inheritance hierarchies, there are none whatsoever in template structures. It's like everything is a void* and the compiler tries to tell you if there'll be a segfault.

All that being said, I use it quite successfully on basic utilities and libraries. Writing high-level functionality or hardware related stuff with it, doesn't seem to cut it for me. Meaning I template my building blocks but build the house the classic way.

Share:
18,307

Related videos on Youtube

AndreasT
Author by

AndreasT

Updated on July 19, 2020

Comments

  • AndreasT
    AndreasT almost 4 years

    I have to make a decision regarding generalization vs polymorphism.

    Well the scenario is standard: I want to make my monolithic interdependent code to be more modular, clean and extensible. It is still in a stage where the change of design principle is doable, and, as I look at it, highly desirable.

    Will I introduce purely virtual base classes (interfaces) or templates?

    I am aware of the basics regarding the template option: less indirection, better performance, more compiling but no late binding, and so on.

    The stl does not use much (or none?) inheritance, and boost doesn't either. But I think those are aimed to be really small basic tools that are used every 2 lines of code by the programmer.

    I consider the inheritance and late binding approach to be more sensible for plug-in style of big pieces of code and functionality that should be interchangeable, updateable etc. after deployment or even during runtime.

    Well my scenario lies somewhat inbetween.

    I dont need to exchange pieces of code on the fly at runtime, compile time is fine. Usually it is also a very central and frequently used piece of functionality, it is not logically seperatable into big blocks.

    This lets me tend somewhat to the template solution. To me it also looks somewhat cleaner.

    Are there any big bad implications, are interfaces still THE way to go? When are they not? Which complies more with standard c++ style?

    I know this is bordering on subjective, but I am really interested in some experiences. I don't own a copy of Scott Meyers effective C++ so I set my hopes on you guys :)

  • sbi
    sbi almost 15 years
    You are certainly right. What I meant, however, (and phrased poorly) is that with in run-time polymorphy, you can be sure that what you get is a deliberate implementation of some interface, while duck typing in compile-time polymorphy might accept any accidental match. How to say this better?
  • AndreasT
    AndreasT over 14 years
    +1 Your answer was very enlightening, but in compliance to the question I accepted jalf's advice. Thanks.
  • Virus721
    Virus721 almost 9 years
    Well I do see one problem using templates rather than interfaces : requirements are totally implicit. When you have to implement pure virtual function, you are given its exact signature. But when you see a template type like _AllocT or Iter you have no idea what your class is required to have nor if it even has to be a class. Your only way to know is by looking for a decent documentation about it, which i did have trouble doing today when trying to create my own stl-compatible allocator class.
  • josesuero
    josesuero almost 9 years
    "You only way to know is by looking for a decent documentation about it" - or by trying to compile and seeing which functions the compiler complains about being unable to find, yes. Also, Concepts are intended to solve this problem. (And even if it had been an interface, you'd still need to find decent documentation. Knowing which functions to override is not enough. You also need to know what their semantics should be, and the interface doesn't tell you that). Still, you are right. There is a reason the language supports both. :)
  • ar2015
    ar2015 over 5 years
    Wouldn't it be neater to erase many templates and replace them with interfaces, if C++ could support that?