c++c++11templatessfinaeenable-if

How can I avoid writing `::value` and `::type` when using `std::enable_if`? [cppx]


Note: This is a question-with-answer in order to document a technique that others might find useful, and in order to perhaps become aware of others’ even better solutions. Do feel free to add critique or questions as comments. Also do feel free to add additional answers. :)


In some of my code, namely the header rfc/cppx/text/String.h, I found the following mysterious snippet:

template< class S, class enable = CPPX_IF_( Is_a_< String, S > ) >
void operator!  ( S const& )
{ string_detail::Meaningless::operation(); }

The operator! is in support of a String class that has an implicit conversion to raw pointer. So I overload (among others) operator! for this class and derived classes, so that inadvertent use of a non-supported operator will give a suitable compilation error, mentioning that it's meaningless and inaccessible. Which I think is much preferable to such usage being silently accepted with an unexpected, incorrect result.

The CPPX_IF_ macro supports Visual C++ 12.0 (2013) and earlier, which finds C++11 using to be mostly beyond its ken. For a more standard-conforming compiler I would have written just …

template< class S, class enable = If_< Is_a_< String, S > > >
void operator!  ( S const& )
{ string_detail::Meaningless::operation(); }

This looks like std::enable_if,

template< class S, class enabled = typename std::enable_if< Is_a_< String, S >::value, void >::type >
void operator!  ( S const& )
{ string_detail::Meaningless::operation(); }

except that the If_ or CPPX_IF_, and its expression, is much more concise and readable.

How on Earth did I do that?


Solution

  • In C++14, variable templates make type traits a lot more comfortable to look at. Combine that with C++11 template aliases, and all the cruft disappears:

    template <typename A, typename B>
    bool is_base_of_v = std::is_base_of<A, B>::value;
    
    template <bool B, typename T = void>
    using enable_if_t = typename std::enable_if<B, T>::type;
    

    Usage:

    template <typename B, typename D>
    enable_if_t<is_base_of_v<B, D>, Foo> some_function(B & b, D & d) { /* ... */ }
    

    "Type" aliases of the form _t are in fact planned as part of the standard library for C++14, see [meta.type.synop].