1

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?

lizarisk
  • 7,562
  • 10
  • 46
  • 70

1 Answers1

2

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks, it works this way, but if I ditch the struct and just use the expression in-place (inside a bigger MPL expression), I still get the same problem. How should I deal with that? – lizarisk May 31 '16 at 17:26
  • @lizarisk I don't know what that means. Perhaps ask a new question with specifics? – Barry May 31 '16 at 17:33
  • I mean, I understood that transforming an alias to a struct delays the template instantiation and solves the problem, but what if I just want use the expression inline (i.e. `remove_reference` in this case)? How should I delay template instantiation? – lizarisk May 31 '16 at 17:37
  • @lizarisk Updated. – Barry May 31 '16 at 17:59
  • Thanks! I dropped this solution because I was trying to use this both as type traits and as a part of a placeholder expression, and that got me confused for some reason. – lizarisk May 31 '16 at 21:49