People who read the original question may be writing structs that use template template parameters as meta functions, as demonstrated in the listing below.
template <int T>
struct integer
{
using value = T;
};
template <class T, class U, template <class...> class Function>
struct binary_op
{
// Works for add_1, but not add_2
using type = typename Function<T, U>::type;
// Works for add_2, but not add_1
using type = Function<T, U>;
};
template <class T, class U>
struct add_1;
template <int T, int U>
struct add_1<integer<T>, integer<U>>
{
using type = integer<T + U>;
};
template <class T, class U>
using add_2 = typename add_1<T, U>::type;
add_1
and add_2
are both meta-functions, let's distinguish
add_1
as an example of nested typedef-style metafunction (which c++03 supported)
add_2
as an example of template alias-style metafunction (which requires c++11)
The binary_op
struct can work either with template alias-style or nested typedef-style metafunctions, but not both. In this answer, I show how such TMP code can be rewritten to avoid this problem.
Suppose that you wish to apply a template template parameter Function
to a parameter pack of values Ts...
. To apply the metafunction, you need either
using type = Function<Ts...>; // template-alias style
or
using type = typename Function<Ts...>::type; // nested typedef style
It would be useful to have another generic metafunction that detects the kind of metafunction that was passed, and applys it accordingly.
The is_alias_metafunction
function, which is implemented below, is a building block for such a facility:
#include <type_traits>
template <class... Ts>
struct sequence;
template <class T>
struct check
{
static constexpr bool value = true;
};
template <
template <class...> class Function,
class S,
class Check = void
>
struct is_alias_metafunction
{
static constexpr bool value = true;
};
template <
template <class...> class Function,
class... Ts
>
struct is_alias_metafunction<
Function,
sequence<Ts...>,
typename std::enable_if<
check<typename Function<Ts...>::type>::value
>::type
>
{
static constexpr bool value = false;
};
Now, we can write a metafunction apply
that applies a template template parameter Function
to the parameter pack Ts...
, regardless of whether Function
is a template alias or a template struct.
template <
bool IsAlias,
template <class...> class Function,
class S
>
struct apply_impl;
template <template <class...> class Function, class... Ts>
struct apply_impl<true, Function, sequence<Ts...>>
{
using type = Function<Ts...>;
};
template <template <class...> class Function, class... Ts>
struct apply_impl<false, Function, sequence<Ts...>>
{
using type = typename Function<Ts...>::type;
};
template <template <class...> class Function, class... Ts>
using apply = typename apply_impl<
is_alias_metafunction<Function, sequence<Ts...>>::value,
Function,
sequence<Ts...>
>::type;
We can now use the apply
metafunction as follows:
using type = apply<Function, Ts...>;
and it will abstract away the difference between 'legacy' metafunctions and modern (c++11) metafunctions.