C++ templates are an exciting addition to the language. They are extremely powerful, but they are also difficult to write, use, read and debug. Some of the more powerful techniques for using them: Traits, StlStyle and compile time recursion, only add to the confusion. This confusion tends to bury templates too deep in the bag of tricks for most people to use them, but mastery of template programming has clear benefits. This page is for discussion of tips, tricks and traps associated with template programming. I'll start out with a description of Traits.
template <typename _Type> class ReleaseTraits { Release(_Type *Resource){Resource->Release();} } class ReleaseTraits<SynchronizationLock> { Release(SynchronizationLock *Resource){Resource->Unlock();} } class ReleaseTraits<MemoryPtr> { Release(MemoryPtr *Resource){delete Resource;} } template <typename _Type> class AutoRelease { AutoRelease<_Type>(_Type *ToRelease?):mToRelease(ToRelease?){} ~AutoRelease<_Type>(){ReleaseTraits<_Type>::Release(mToRelease){} _Type mToRelease; }
This and the next few items would belong in TemplatesDiscussion
What's with the template love fest? I hate templates. To me, they are a poor work around for the lack of a base class. I mean, I like C++ for the most part, but templates just infuriate me so much. I'm tempted to go create a class library with a base class every time I have to use templates. If I could change only one thing about C++, I'd chuck the templates and introduce a base class.
C++ has a universal base class - it's called void*. These template aficionados are probably just hung up on type safety.
In a static language, your best friend is the compiler. In a static language, strong types lend strength to your best friend.
StaticPolymorphism is one of the CeePlusPlusIdioms. That is: As for void*, and base classes:
Well, technically, void* doesn't work as a base class. It doesn't deal with destruction via derived class destructors, or anything else useful. You can't dispatch methods via void*, nor access member variables. And, of course, there is no 'reflection', so you can't find out what a void * is pointing at programatically: YouJustHaveToKnow?.
But really templates are a work around (poor or not) for the lack of aspects, which cut across class boundaries. A more generic environment must either use dynamic typing (Smalltalk, Python, etc) or else needs generics somewhat along the lines of aspects and templates.
Type safety is a great good in its own right, though it can add some complexity (which is bad). But not having dynamic typing, we must have stronger typing. C++ fails in being too loose, not too strict.
Well, one place that templates really shine is in numerical work, where we can't afford the cost of vpointers. A good discussion of technique etc. can be found at the object oriented numerics page http://oonumerics.org/oon/.
I am a big fan of templates because they help me to stick to the principle of OnceAndOnlyOnce. I think that they are a great language feature. Unfortunately, template support wrt compilers and debuggers still has a way to go in my experience.
The following is neat trick that uses both template functions and template classes to implement callback objects.
Suppose you need to implement the ObserverPattern. Generally you would inherit from an abstract Observer base class that had a pure virtual function (called, say, notify()) that you would override and then pass yourself to the Subject object. The Subject would keep track of its observers and invoke notify() on them when required.
But suppose that you don't want to inherit from the provided observer class. Why should you have to expose what is simply an implementation detail in your exported header file and cause all of your clients to have to recompile ?
The solution is to use a callback object. This object keeps a pointer to your object and a pointer to an arbitrary method on your object (of the correct signature) and can be passed to the Subject object. When it is called back it invokes the method you have specified on your object.
This allows you to write code like:
subject->registerCallback(makeCallback(this, X::method1));where X is your class name and method1() is the method you want called back.
Then when the subject object notifies its observers, your object's ?method1()? will be invoked.
The following code shows how this works (I have tried to keep it as simple as possible):
struct Callback { virtual void notify() = 0; }; template<class T> struct Callback_T: public Callback { Callback_T(T* object, void (T::*method)()) : myObject(object), myMethod(method) {} virtual void notify() { (myObject->*myMethod)(); } T* myObject; void (T::*myMethod)(); }; template<class T> Callback* makeCallback(T* object, void (T::*method)()) { return new Callback_T<T>(object, method); }The idea of the template function is to make the syntax for creating these objects slightly easier. Unlike template classes, the template arguments for template functions are deduced from the actual arguments passed to them. This means that our client code doesn't need to have any template syntax in it at all. In this simple example it doesn't add much but when you add extra stuff that requires more template arguments it helps make the code easier to understand.
James, I like this. Who deletes the Callback object above? And when?
It can be easily managed with SmartPointer.
At the risk of offending the nice professional politeness around here, I don't like it much. I don't think that the subject should need to know that it is a subject (not at header file level) I think it is a real issue to tidy up the Callbacks when the Observers die. boost::shared_pointer / weak_pointer can handle it, I gather.... but this depends on all clients using that idiom. I have derived Observers from a basic housekeeping class, to catch the destructor, but really it would be nice for Observers not to have to know they are observers. You can do this with an evil hack by in place constructing a new version of the observer on top of itself, with the tidy up functionality in its destructor, on registration. But this is not very nice, and only works for observers on the heap. I am a big fan of templates too but I would never have used this as an advertisement! -- BillWeston
Experienced C++ programmers should delight in templates. Newbies and weak journeymen should probably learn to use them without writing them. The syntax for writing templates is staggeringly complex for people who have really just mastered colon-initializer lists.
A further recommendation: teach code users to typedef their stl classes and iterators and work through the typedefs. They get to avoid the <> syntax, and you get to replace poorly-thought-out template instantiations in one and only one location. -- MichaelHill
I've used ExpressionTemplates to great effect, both writing my own and using a library (BlitzPlusPlus) built on them. Usage is usually simple. For example:
Vector x( 10 ), y( 4 ); Matrix a( 10, 10 ); // initialize x, a here y = ( a * x ).slice( 4, 8 );A non-template implementation would build a temporary 10-element vector from a * x, then slice out the requested 4 elements and stuff them into y.
An expression template implementation has a * x returning a placeholder object, which represents the product without computing it. The slice member function of this object returns another placeholder, which represents taking a slice of a computation. The assignment operator triggers actual computation.
In the example above, only four elements end up being computed. In an example with more operations, the savings in computation time and in storage of temporaries can be large.
Although the usage can be straightforward, the implementation is messy. For example, the declarations used in the above might look like:
namespace VectorMatrixNamespace { template< class T > class SliceRepresentation; template< class LeftT, class RightT > class ProductRepresentation { public: SliceRepresentation< ProductRepresentation< LeftT, RightT > > slice( int begin, int end ); .... }; // putting the operators in a namespace makes them private to our types: template< class LeftT, class RightT > ProductRepresentation< LeftT, RightT > operator * ( LeftT & left, RightT & right ); template< class RightT > Vector & operator = ( RightT & right ); };In addition to the declarations above, we might need partial specializations to deal with slices of matrices, slices of vectors, etc.
Is this really specific to templates? Perhaps placeholder objects could also be used with regular classes? It also sounds a lot like lazy evaluation in functional languages.
There isn't a page for quite what I want so I'll start TemplatesAndRunTimeTypeInformation
-- JohnFletcher