Please consider metafunctions like
#include <type_traits>
template <typename T, T N, T M>
struct Sum : std::integral_constant <T, N + M> {};
template <typename T, T N, T M>
struct Product : std::integral_constant <T, N * M> {};
Their result can be extracted through the ::value
member:
static_assert (Sum <int, 3, 4>::value == 7, "3 + 4 == 7");
static_assert (Product <int, 2, 5>::value == 10, "2 * 5 == 10");
Both metafunctions have a similar static signature. That is, they associate a T
to every pair of T
's where T
is subject to the same restrictions as those imposed by std::integral_constant
and either being summable or multipliable. So we can create a generic metafunction to do the evaluation.
template <typename T, template <typename U, U, U> class F, T N, T M>
struct EvaluateBinaryOperator : std::integral_constant <T, F <T, N, M>::value> {};
static_assert (EvaluateBinaryOperator <int, Sum, 3, 4>::value == 7, "3 + 4 == 7");
static_assert (EvaluateBinaryOperator <int, Product, 2, 5>::value == 10, "2 * 5 == 10");
When used solely in this form, it feels redundant to to pollute Sum
and Product
with the structure of an std::integral_constant
. To show you that we can do without indeed, please consider the following:
template <typename T, T N, T M, T R = N + M>
struct Sum;
template <typename T, T N, T M, T R = N * M>
struct Product;
template <typename> struct EvaluateBinaryOperator;
template <typename T, template <typename U, U, U, U> class F, T N, T M, T R>
struct EvaluateBinaryOperator <F <T, N, M, R> > : std::integral_constant <T, R> {};
static_assert (EvaluateBinaryOperator <Sum <int, 3, 4> >::value == 7, "3 + 4 == 7");
static_assert (EvaluateBinaryOperator <Product <int, 2, 5> >::value == 10, "2 * 5 == 10");
Instead of using members of Sum
and Product
, we specialize on a default argument and extract it only in EvaluateBinaryOperator
. As an added bonus, Sum
and Product
can remain without definition, rendering them trivially non-inferrable and non-constructable and the syntax looks much cleaner too. Now, here's the catch. What if we would like all our metafunctions to have a uniform static interface? That is, what if we introduce
template <typename...> struct Tuple;
template <typename T, T> struct Value;
and require all our metafunctions to look like template <typename> struct
? For instance,
template <typename> struct Sum;
template <typename T, T N, T M>
struct Sum <Tuple <Value <T, N>, Value <T, M> > > :
std::integral_constant <T, N + M> {};
template <typename> struct Product;
template <typename T, T N, T M>
struct Product <Tuple <Value <T, N>, Value <T, M> > > :
std::integral_constant <T, N * M> {};
Now, we would like to transform them to something like:
template <typename, typename> struct Sum;
template <typename T, T N, T M, typename R = Tuple <Value <T, N + M> > >
struct Sum <Tuple <Value <T, N>, Value <T, M> >, R>;
template <typename, typename> struct Product;
template <typename T, T N, T M, typename R = Tuple <Value <T, N * M> > >
struct Product <Tuple <Value <T, N>, Value <T, M> >, R>;
Such that we can extract values with
template <typename> struct Evaluate;
template <template <typename, typename> class F, typename I, typename O>
struct Evaluate <F <I, O> > {
typedef O Type;
};
static_assert (std::is_same <
Evaluate <Sum <Tuple <Value <int, 3>, Value <int, 4> > > >::Type
Tuple <Value <int, 7>
>, "3 + 4 == 7");
static_assert (std::is_same <
Evaluate <Product <Tuple <Value <int, 2>, Value <int, 5> > > >::Type
Tuple <Value <int, 10>
>, "2 * 5 == 10");
Those of you familiar with the C++ standard will immediately point to 14.5.5/8: "The template parameter list of a specialization shall not contain default template argument values.", accompanied by the teasing footnote: "There is no way in which they could be used.". Indeed, feeding just about any modern compiler this code yields a compiler error on the Sum
and Product
template specializations about violation of the standard. Apart from proving the aforementioned footnote to lack the imagination of the author; we've created ourselves a valid use case for them.
My question can now be put: Are there any other ways to achieve a similar effect where Sum
and Product
remain undefined / incomplete types, thereby trivially being non-inferrable and non-constructable, while still carrying responsibility for performing the operation? Any suggestions are welcome. Thanks in advance.
Aucun commentaire:
Enregistrer un commentaire