7

In the following code (simplified for demonstration):

  namespace mpl = boost::mpl;

  using if1 = mpl::if_<std::is_same<double, mpl::_1>, double, void>;
//using if2 = mpl::if_<std::is_same<double, mpl::_1>, typename std::common_type<double, mpl::_1>::type, void>;

  using apply1 = boost::mpl::apply<if1, double>::type;
//using apply2 = boost::mpl::apply<if2, double>::type;

In std::is_same<double, mpl::_1>, the placeholder is correctly substituted with double, as if the instantiation were explicitly std::is_same<double, double> which results in the correct/expected behavior.

However, in std::common_type<double, mpl::_1>, the placeholder is not substituted, as if the instantiation were explicitly std::common_type<double, mpl_::arg<1>>, which causes the following error since there is obviously no "common" type:

error: incompatible operand types ('double' and 'mpl_::arg<1>')


The Question: Why is the mpl::_1 placeholder correctly transformed/substituted with double in std::is_same, but not in std::common_type? And is there a workaround?


etherice
  • 1,761
  • 15
  • 25
  • I guess you need `::value` after `std::is_same<...>` and maybe `::type` after `mpl_if_<...>`? – leemes Nov 05 '13 at 19:21
  • @leemes: That's not the issue. `mpl::if_` parameter 1 takes an `integral_constant` (which `std::is_same` subclasses). The `if1` works correctly, and `apply1` is `double` as expected. The problem is `if2` with `std::common_type` as explained in the question. – etherice Nov 05 '13 at 19:24

1 Answers1

6

You're forcing too eager instantiation by accessing the nested ::type of std::common_type before applying the lambda. Replace typename std::common_type<double, mpl::_1>::type with std::common_type<double, mpl::_1> and you should be all good.

EDIT: Apologies for the bad advice. I didn't see quite what you were doing. Trouble is that mpl::apply will turn your placeholder expression into a lambda expression by first running it through mpl::lambda. That will cause std::common_type<double, mpl::_1> to be wrapped in an mpl::protect, which will block it from being evaluated in the first pass, and mpl::if_ won't evaluate it in the second pass because it treats its second and third parameters as plain types, not placeholder expressions. You can use mpl::bind to force std::common_type to be evaluated before mpl::if_. That way, mpl::if_ sees if_<some-condition, double, void>, and all is right again.

#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/bind.hpp>
#include <boost/mpl/quote.hpp>

namespace mpl = boost::mpl;

template<typename T, typename U> using common_type2 = std::common_type<T,U>;
using if2 = mpl::if_<
                std::is_same<double, mpl::_1>,
                mpl::bind<mpl::quote2<common_type2>, double, mpl::_1>,
                void>;
using apply2 = boost::mpl::apply<if2, double>::type;
static_assert(std::is_same<apply2, double>::value, "works");

HTH!

Eric Niebler
  • 5,927
  • 2
  • 29
  • 43
  • 1
    Thanks- using `bind` to delay the instantiation of `common_type` (until after `apply` transforms the placeholders) works like a charm. – etherice Nov 06 '13 at 12:11
  • Why does mpl wrap `std::common_type` in `mpl::protect`? Is there a good reason for this? – Paul Fultz II Nov 06 '13 at 14:03
  • I don't know the answer off the top of my head. If I discover the reason, I'll edit my response. Or maybe someone else can fill us in. – Eric Niebler Nov 06 '13 at 16:32