C++ helps you attain information hiding in your programs. You just specify the interface in one file (sorry, "translation unit") with a .h suffix; that's the file you #include. The implementation goes in another file with a .cpp, .cxx, .C, .c++ or .cc suffix (see "C++: standard names for source files" for more information on how to pick the correct suffix for your C++ source code). That way, the application programmer using the library doesn't need to see the actual code implementing the interface. This is a Good Thing, and C++ is all for such Good Things.

C++ also supports parametrised programming through function templates. You can write just one function which looks like this (in practice, the standard library defines std::swap, which does the same:

template<typename T> void swap(T& a, T& b) {
  T c = a;
  a = b;
  b = c;
}
which can swap 2 variables of any type, and has a convenient interface. This is really cool.

C++ programmers really like this example. I mean, REALLY. "Let's see you write that in C," they sneer, a grin wrapping itself around their ugly little faces.

(Note: when I wrote this, I hadn't known that this example appears in the swap node to show just how groovy C++ templates really are. Ooops.)

Well, OK, but first let's see them write it in C++. I mean, you'd think that the above is code, so it goes in the implementation file, and the interface file just contains a line

template<typename T> void swap(T& a, T& b);
Well, you'd think that, but you'd also be wrong. You see, separate compilation doesn't apply to template functions. They go in the header file. Reduced to being glorified #define macros, the compiler has to see them separately in each source file (oops, sorry, "translation unit").

The same goes for class templates, too: you can use them, but you end up implementing the entire class in the header file. So much for information hiding.

At this point, the dedicated C++-head starts sweating. "But wait!" they scream, as you lean in for the kill, "the new export keyword solves all that!" New, it is. And it may well solve everything. I don't know, and neither do they. Because there are serious issues with implementing it. Doing it properly probably requires the linker to be really smart, maybe even to be able to call back to the compiler. So nobody's ever actually written code with the legendary export keyword...

Actually, there's two related issues here, let's separate them. The first is to separate interface from implementation, the second separate compilation. 1. It is a good thing to separate interface from implementation. This can be achieved for templates by putting declarations in header files and definitions in implementation files, for example with a .cpp extension. Now, the compiler needs to see the template definition, so the implementation file must be #included. An easy way to get this is to put something like
#if defined(INCLUDE_TEMPLATE_DEFINITIONS)
#include "myclass_t.cpp"
#endif
at the end of myclass_t.hpp. *someone tell me how to revert to the above font, please* This doesn't prevent the programmer from looking at the definition, but at least she isn't forced to see it when she reads the interface. 2. Yes, separate compilation requires the linker to be smart. Again, there's two aspects here, being able to do it at all and making it convenient. First, as far as I can see, truly separate compilation (think of using a library) requires the template definition to be available. This effectively means that the source must be available to the linker, which will pass it to the compiler when a template needs to be instantiated. Unless the source is delivered beside the library file, it must be included inside of the library file. This appears to be the straightforward way to implement the export keyword. Second, separate compilation implies incremental compilation (by compilation unit). And this should apply to templates, too. If a template has been instantiated for a particular template argument type by one compilation unit, it should not be compiled again if a second compilation unit instantiates it. Some if not many compiler/linker duos use a template repository to achieve this. These linkers are smart enough to call the compiler for help, should they realize that a new template instance is needed.

There is a very real distinction when writing code such as the above between language extension and program implementation. The above code implements swap, a really basic operation, using templates. Although hiding implementation is Good (tm), in this case it's not really important: you know how swap will work. The header really still isn't giving out any implementation secrets.

In my experience (which is by no means definitive - msg me!), this is the general case. Templates are solely a tool for adding value. They should only be used to wrap a flexible implementation with variable types, not to implement the implementation itself. Part of the beauty of templates is that they encapsulate metaprogramming. In other words, you should have some kind of implementation that works without templates.

To the casual observer, my claim is patently false. The only templates the average newbie sees are the STL. The STL has no ".cpp" implementation files. But, is the STL implementing a program, or merely extending the language?

Templates serve the purpose of bridging gaps where the C++ language is insufficient to express some generalization. Generalization is what templates do, and that is a separate thing from implementation. STL is a bunch of really useful generalizations, but not complete implementations. To sort a vector, you first have to define comparators. If you talk about how to sort, without knowing what you are sorting, you should not really have any implementation details to hide - it's too basic. The underlying reason for hiding encapsulated implementation is that programmers shouldn't rely on side effects of internal details. Something as simple as sorting should not have side effects.

My argument thus far is that generic-ness excludes the need for privacy. That's unfortunately pretty idealistic. The edges are fuzzy. For example, how does the sort behave with a partial ordering of distinct items? There is an orthogonal fuzzy edge, though, which allows the programmer to (attempt to) balance the implementation.

In most cases, including many in the STL, C++ supports enough high-level programming to encapsulate the engine of even a generic algorithm in the implementation file. This requires writing the header as a discrete type-translation layer. Function pointers can package up code which needs to be called, pointer to members and regular pointers package data. More abstract values can be produced with sizeof or offsetof. With elbow grease, actual runtime algorithms can move into .cpp files, compiled once and nicely hidden.

This is still not a particularly strong argument. To prove that templates do not force the exposure of any particular kind of implementation detail, I would have to define some kind of multidimensional space for all implementation details. I'm way too lazy to do that. As you can see, I'm too lazy even to provide an example of proper template factoring as I've defined it. All I can state is that my ideal template methodology differs from the way STL implementations are universally written, and that my methodology has been practicable for me so far.

References: having written a few STL-style templates, such as an allocator, and having systematically parameterized an existing system (see why C++ doesn't suck).

Log in or register to write something here or to contact authors.