I am having a hard time understanding why the following simple program won't compile. I have a variadic template class (my_type
below) which I want to use to transform an mpl vector. The following snippet leads to a compilation error "/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:38:19: 'apply' following the 'template' keyword does not refer to a template".
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform.hpp>
template <class... T>
struct my_type{};
using namespace boost::mpl;
using test_type = vector<int, double>;
// expected result is vector< my_type<int>, my_type<double> >
using result_type = transform< test_type, my_type<_> >::type;
int main() {
}
Making my_type
take a single template parameter works fine, but I would like to understand why the variadic version does not work. Thank you in advance!
The second argument expected by transform is a unary operation which you are not passing.
In your case, my_type
is not a metafunction
that mpl expects since it is making use of a variadic parameter list.
A metafunction
in the simplest case exposes a type
typedef or a ststic bool
value
. For eg:
template <typename T>
struct add_pointer {
using type = T*;
};
Note how add_pointer
converts the provided template parameter. This is an example of unary
operation since it takes only one template parameter T
.
In your example my_type
is purely a type and cannot be used as a metafunction or operation as it does not satisfy the criteria of the metafunction
as required by the transform
metafunction which is have a type
field to indicate the transformed type.
In some cases, a simple temmplate type is converted into a metafunction
as explained in the Detailed Reasoning
section below.
Doc reference : http://www.boost.org/doc/libs/1_31_0/libs/mpl/doc/ref/Reference/transform.html
Code:
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/equal.hpp>
#include <type_traits>
#include <typeindex>
#include <iostream>
template <class... T>
struct my_type{};
using namespace boost::mpl;
using test_type = vector<int, double>;
template <typename T>
struct add_my_type {
using type = my_type<T>;
};
using result_type = typename transform< test_type, add_my_type<_1> >::type;
int main() {
static_assert (equal<result_type, vector< my_type<int>, my_type<double> >>::value, "Nope!!");
std::cout << typeid(result_type).name() << std::endl;
}
Detailed Reason
The reason explained above is pretty brief in should be enough to answer the question. But lets dig into the details as much as I can.
DISCLAIMER: I am no expert in boost::mpl.
As per the comment below by the OP, the original code works if we change my_type
to:
template <class T>
struct my_type{};
But that doesn't go well with what I mentioned previously i.e operation
needs a type
identifier. So, lets see, what mpl is doing under the hood:
struct transform
somewhat looks like:
template<
typename Seq1 = mpl::na
, typename Seq2OrOperation = mpl::na
, typename OperationOrInserter = mpl::na
, typename Inserter = mpl::na
>
struct transform {
boost::mpl::eval_if<
boost::mpl::or_<
boost::mpl::is_na<OperationOrInserter>,
boost::mpl::is_lambda_expression<my_type<mpl_::arg<1> > >,
boost::mpl::not_<boost::mpl::is_sequence<my_type<mpl_::arg<1> > > >,
mpl_::bool_<false>,
mpl_::bool_<false>
>,
boost::mpl::transform1<
boost::mpl::vector<int, double>,
my_type<mpl_::arg<1>>,
mpl_::na
>,
boost::mpl::transform2<boost::mpl::vector<int, double>,
my_type<mpl_::arg<1> >,
mpl_::na, mpl_::na>
>
};
The important part to look here is the is_lambda_expression
metafunction which basically checks if your Operation
meets the requirement of a metafunction
.
After applying some heavy macro and template machinery and specializations, the above check synthesises below struct:
template<
typename IsLE, typename Tag
, template< typename P1 > class F
, typename L1
>
struct le_result1
{
typedef F<
typename L1::type
> result_;
typedef result_ type;
};
Here, F
is your my_type
and L1
is the placeholder
. So, in essence the above struct is nothing but add_my_type
which I had shown in my initial response.
If I am correct till now, le_result1
is the operation
that would be performed on your sequence
.