Sometimes, when multiple types are sent as template parameters, some information about the types needs to be interrogated to be able to configure another property of the template. The STL’s type traits header does a good job of supplying many of the more useful ones, but there is room for extension. Say, for instance, the template implements a type of variant container; a type of smart union. The container may store one of several types declared in the template parameter list. We need to allocate storage for this, but how determine the size?
template typename T1, typename T2, typename T3>
struct Buffer
{
char buffer[???]; // Wouldn't it be nice to have something simple here,
// rather than manually typing a lot of nested sizeof() boilerplate?
};
We can use template meta programming to help us here.
Here I will show you two variations; one for pre C++11 compilers that don’t support variadic templates and one for those that do.
Let’s say that we want to be able to handle up to four types. The ‘= void’ allows one, two, three or four types to be specified.
template <typename T1, typename T2 = void, typename T3 = void, typename T4 = void>
strut Largest
{
};
First, let’s create a helper template to determine the largest of two types.
These templates will define their ‘type’ to either that of TrueType or FalseType dependent on the condition supplied.
Next we create a recursive typedef that chooses the largest type between the first template parameter type and all of the rest. We typedef ‘all of the rest’ for clarity.
// Define 'LargestOther' as 'Largest' with all but the first parameter.
typedef typename Largest<T2, T3, T4>::type LargestOther;
Now the recursive bit.
typedef typename ChooseType<(sizeof(T1) > sizeof(LargestOther)), // Boolean
T1, // TrueType
LargestOther> // FalseType
::type type; // The largest type of the two.
The compiler will recursively parse the template until there is one type to test. Here we need something to stop the compiler attempting to parse any further.
What we do is to add a specialisation for the final single type.
template <typename T1, typename T2 = void, typename T3 = void, typename T4 = void>
struct Largest
{
private:
// Declaration.
template <const bool Boolean, typename TrueType, typename FalseType>
struct ChooseType;
// Specialisation for 'true'.
// Defines 'type' as 'TrueType'.
template <typename TrueType, typename FalseType>
struct ChooseType<true, TrueType, FalseType>
{
typedef TrueType type;
};
// Specialisation for 'false'.
// Defines 'type' as 'FalseType'.
template <typename TrueType, typename FalseType>
struct ChooseType<false, TrueType, FalseType>
{
typedef FalseType type;
};
public:
// Define 'LargestOther' as 'Largest' with all but the first parameter.
typedef typename Largest<T2, T3, T4>::type LargestOther;
// Set 'type' to be the largest of the first parameter and any of the others.
// This is recursive.
typedef typename ChooseType<(sizeof(T1) > sizeof(LargestOther)), // Boolean
T1, // TrueType
LargestOther> // FalseType
::type type; // The largest type of the two.
};
//***************************************************************************
// Specialisation for one template parameter.
//***************************************************************************
template <typename T1>
struct Largest<T1, void, void, void>
{
typedef T1 type;
};
Recent Comments