Suggestions for convenience aliases in <type_traits>

Jonathan Wakely jwakely@redhat.com
Tue Jun 10 11:17:00 GMT 2014


On 10/06/14 12:47 +0200, Daniel Krügler wrote:
>2014-06-10 12:30 GMT+02:00 Jonathan Wakely <jwakely@redhat.com>:
>> One of the reasons I've avoided adding the last two is that I'm not
>> sure how to name them appropriately.
>>
>> In Boost.MPL the trait that takes a type parameter as its first
>> template parameter is named enable_if, and the version taking a
>> non-type bool parameter is named enable_if_c, but in the std::lib we
>> dropped the _c suffix.
>
>IMO the comparison with the corresponding boost traits is not a good
>one, because alias templates are not evaluated lazily, so
>__enable_if_t_ or __conditional_t_ leads to the incorrect assumption
>not to evaluate the provided predicate.

OK, avoiding names with misleading implications is important.

>> The alias templates above are named to be consistent with the similar
>> C++14 aliases, but with an additional _ suffix, which is consistent
>> with the invaluable __and_, __or_ and __not_ aliases we already have
>> in <type_traits> (which also have type parameters not bools).

(I don't know why I wrote that __and_ etc. were aliases there, I know
they're not as they need to be partially specialized ... stupid brain.)

>> Is that reasonable?
>
>Personally I consider these as bad examples for alias templates and
>suggest to provide corresponding class templates instead (such __and,
>__or, __not).

Do you mean like this?

  template<typename _If, typename _Then, typename _Else>
    struct __conditional_
    : conditional<_If::value, _Then, _Else>
    { };

With that I lose the convenience of the C++14 '_t' aliases and have to
write 'typename' and '::type' every time I use it.

Or do you mean like this:

  template<typename _If, typename _Then, typename _Else>
    struct __conditional_t_
    : conditional<_If::value, _Then, _Else>::type
    { };

In that case doesn't it also evaluate the prediate immediately, and so
has no advantage over the alias template.

Or do you suggest re-implementing it completely, not re-using
std::conditional?

My use cases for the suggested aliases all involve immediate
evaluation of the predicate anyway, e.g. for <experimental/any>

  template<typename _Tp>
    struct _Manager_internal;

  template<typename _Tp>
    struct _Manager_external;

  template<typename _Tp, typename _Safe = is_nothrow_copy_constructible<_Tp>,
           bool _Fits = (sizeof(_Tp) <= sizeof(_Storage))>
    using _Internal = std::integral_constant<bool, _Safe::value && _Fits>;

  template<typename _Tp>
    using _Manager = conditional_t<_Internal<_Tp>::value,
                                   _Manager_internal<_Tp>,
                                   _Manager_external<_Tp>>;

  template<typename _Tp, typename _Decayed = decay_t<_Tp>>
    using _Decay = enable_if_t<!is_same<_Decayed, any>::value, _Decayed>;

  template <typename _ValueType, typename _Tp = _Decay<_ValueType>,
            typename _Mgr = _Manager<_Tp>>
    any(_ValueType&& __value);

With the suggested aliases _Internal and _Manager become:

  template<typename _Tp, bool _Fits = (sizeof(_Tp) <= sizeof(_Storage))>
    using _Internal = __and_<std::__boolean_constant<bool, _Fits>,
                             is_nothrow_copy_constructible<_Tp>>;

  template<typename _Tp>
    using _Manager = __conditional_t_<_Internal<_Tp>,
                                      _Manager_internal<_Tp>,
                                      _Manager_external<_Tp>>;

(Those are only slightly simpler, but the difference adds up over many
files to be more significant).

Where conditional<> is used in the constructor I want the predicate to
be evaluated immediately, or am I missing something fundamental about
how aliases interact with SFINAE here?

What would I gain from:

  template<typename _Tp>
    using _Manager = typename __conditional_<
      _Internal<_Tp>, _Manager_internal<_Tp>, _Manager_external<_Tp>
      >::type;

?

Thanks for your input.



More information about the Libstdc++ mailing list