lizarisk
lizarisk

Reputation: 7820

How to use nested metafunctions in Boost.MPL?

I have a simple metafunction:

template <typename T>
using is_const_lvalue_reference = mpl::and_<
    std::is_lvalue_reference<T>,
    std::is_const<typename std::remove_reference<T>::type>
>;

Apparently, it doesn't work if T is an MPL placeholder because remove_reference is evaluated for the placeholder class instead of the substituted type. How to do this correctly to be able to use this metafunction in MPL algorithms?

UPDATE: The suggested solution was to replace the alias with a struct, which will delay the template instantiation in std::remove_reference. The question is, how to delay the instantiation inline, not using any helper structs?

template <typename Sequence>
using are_const_lvalue_references = mpl::fold<
    Sequence,
    mpl::true_,
    mpl::and_<
        mpl::_1,
        mpl::and_<
            std::is_lvalue_reference<mpl::_2>,
            std::is_const<typename std::remove_reference<mpl::_2>::type>
        >
    >
>;

This example will apparently fail for the same reason. What should I change to make it correct?

Upvotes: 1

Views: 130

Answers (1)

Barry
Barry

Reputation: 302718

It doesn't work to write type traits as aliases in this way because they get instantiated immediately. is_const_lvalue_reference<_1> is exactly mpl::and_<std::is_lvalue_reference<_1>, std::is_const<_1>> (since _1 isn't a reference type) - that's always false since lvalue references aren't const. Pretty tricky way to write false_ though!

Instead, you have to delay instantiation. Just make your type trait inherit from mpl::and_ instead of aliasing it:

template <class T>
struct is_const_lvalue_reference
    : mpl::and_<
        std::is_lvalue_reference<T>,
        std::is_const<std::remove_reference_t<T>>
        >
{ };

This way, std::remove_reference_t<T> won't get instantiated unless we actually try to access is_const_lvalue_reference<T>::type - which won't happen until the _1 gets substituted for the real type in apply.


Alternatively, since apply<> will invoke ::type where it finds placeholders, you can just drop the explicit invocation of ::type yourself. So this works:

BOOST_MPL_ASSERT(( mpl::apply<
    std::is_const<std::remove_reference<_1>>,
    int const&
    > ));

or with the original expression:

BOOST_MPL_ASSERT(( mpl::apply<
    mpl::and_<
        std::is_lvalue_reference<_1>,
        std::is_const<std::remove_reference<_1>>
    >,
    int const&
    > ));

Note that this construction doesn't work as a type trait though.

Upvotes: 2

Related Questions