Templates are a good candidate for coping with combinatorial behaviour's because they generate code at compile time based on the types provided by the user.
Class templates are customizable in ways not supported by regular classes. If you want to implement a special case, you can specialize any member functions of a class template for a specific instantiation of the class template. For example, if the template is SmartPtr<T>, you can specialize any member function for, say, SmartPtr<Widget>. This gives you good granularity in customizing behaviour.
Furthermore, for class templates with multiple parameters, you can use partial template specialization. Partial template specialization gives you the ability to specialize a class template for only some of its arguments. For example, given the definition
template< class T, class U> class SmartPtr { ... };
you can specialize SmartPtr<T, U> for Widget and any other type using the following syntax:
template< class U> class SmartPtr<Widget, U> { ... };
The innate compile-time and combinatorial nature of templates makes them very attractive for creating design pieces. As soon as you try to implement such designs, you stumble upon several problems that are not self-evident:
1. You cannot specialize structure. Using templates alone, you cannot specialize the structure of a class (its data members). You can specialize only functions.
2. Specialization of member functions does not scale. You can specialize any member function of a class template with one template parameter, but you cannot specialize individual member functions for templates with multiple template parameters. For example:
template <class T> class Widget
{
void Fun() { .. generic implementation ... }
};
// OK: specialization of a member function of Widget
template <> Widget<char>::Fun()
{
... specialized implementation ...
}
template <class T, class U> class Gadget
{
void Fun() { .. generic implementation ... }
};
// Error! Cannot partially specialize a member class of Gadget
template <class U> void Gadget<char, U>::Fun()
{
... specialized implementation ...
}
3. The library writer cannot provide multiple default values. At best, a class template implementer can provide a single default implementation for each member function. You cannot provide several defaults for a template member function.
Now compare the list of drawbacks of multiple inheritance with the list of drawbacks of templates. Interestingly, multiple inheritance and templates foster complementary trade-offs. Multiple inheritance has scarce mechanics; templates have rich mechanics. Multiple inheritance loses type information, which abounds in templates. Specialization of templates does not scale, but multiple inheritance scales quite nicely. You can provide only one default for a template member function, but you can write an unbounded number of base classes.
This analysis suggests that a combination of templates and multiple inheritance could engender a very flexible device, appropriate for creating libraries of design elements.