// print the name of the largest of my_types
std::cout
<< typeid(largest<my_types>::type).name()
<< std::endl
;
}
There are several things worth noticing about this code:
• It uses a few ad-hoc, esoteric techniques, or “hacks”. The default template argument choose1 (labeled “hands
off!”) is one example. Without it, we would have needed yet another template to provide the implementation of
choose_larger, or we would have had to provide the computation explicitly as a parameter to the template -
perhaps not bad for this example, but it would make choose_larger much less useful and more error-prone.
The other hack is the derivation of a specialization of largest from choose_larger. This is a code-saving
device which allows the programmer to avoid writing “typedef typename ...::type type” in the tem-
plate body.
• Even this simple metaprogram uses three separate partial specializations. The largest metafunction uses two
specializations. One might expect that this indicates there are two termination conditions, but there are not: one
specialization is needed simply to deal with access to the sequence elements. These specializations make the
code difficult to read by spreading the definition of a single metafunction over several C++ template definitions.
Also, because they are partial specializations, they make the code unusable for a large community of C++ pro-
grammers whose compilers don't support that feature.
While these techniques are, of course, a valuable part of the arsenal of any good C++ metaprogrammer, their use
tends to make programs written in what is already an unusual style harder-to-read and harder-to-write. By encapsu-
lating commonly-used structures and dealing with loop terminations internally, the MPL reduces the need for both
tricky hacks and template specializations.
1.3. Why metaprogramming?
It's worth asking why anyone would want to do this. After all, even a simple toy example like the factorial metafunc-
tion is somewhat esoteric. To show how the type computation can be put to work, let's examine a simple example.
The following code produces an array containing all possible permutations of another array:
// can't return an array in C++, so we need this wrapper
template< typename T >
struct wrapper
{
T x;
};
// return an array of the N! permutations of 'in'
template< typename T >
wrapper< typename permutation_holder<T>::type >
all_permutations(T const& in)
{
wrapper<typename permutation_holder<T>::type> result;
// copy the unpermutated array to the first result element
unsigned const N = sizeof(T) / sizeof(**result.x);
std::copy(&*in, &*in + N, result.x[0]);
// enumerate the permutations
unsigned const result_size = sizeof(result.x) / sizeof(T);
for (T* dst = result.x + 1; dst != result.x + result_size; ++dst)
{
T* src = dst - 1;
std::copy(*src, *src + N, *dst);
std::next_permutation(*dst, *dst + N);
}
return result;
6