Boost logo

Boost Users :

Subject: Re: [Boost-users] puzzle with enable_if
From: Gevorg Voskanyan (v_gevorg_at_[hidden])
Date: 2010-04-16 02:14:37


John Dlugosz wrote:
> >
> > Move the integral constant type you are deriving from to the
> > template
> > parameter list for SFINAE to apply. In this case that'll
> > be:
>
> > ...
> > Please let me know if this works for
> > you.

> That looks a lot cleaner than what I feared, breaking it
> into helper classes. I'll try it.
> But I can't help but wonder why the
> bottom one (direct use of enable_if) does work. It looks like the code is
> pretty much the same as what I what I wrote.

The difference is whether the template parameter substitution causes an invalid template argument or an invalid resulting function type. Let's consider the three cases one by one.

1. Original version - condition placed inline in enable_if

template <typename T, typename D>
typename boost::enable_if_c<
  is_Universal_time_API<T>::value &&
  (std::tr1::is_same<typename T::duration_t,D>::value ||
    std::tr1::is_same<duration,D>::value ||
    std::tr1::is_same<linear_duration,D>::value ||
    is_dimensional_time<D>::value)
, T >::type
operator+ (const T& t1, const D& d1)

If T is int, the return type of the function would be invalid because of "Attempting to use a type that is not a class type in a qualified name.", and we have SFINAE.

2. First attempt at factoring the condition out for it to be reused - simply deriving from integral_constant< bool, Condition >

template <typename T, typename D>
struct is_proper_TD_types : std::tr1::integral_constant<bool,
    is_Universal_time_API<T>::value &&
      (std::tr1::is_same<typename T::duration_t,D>::value ||
      std::tr1::is_same<duration,D>::value ||
      std::tr1::is_same<linear_duration,D>::value ||
      is_dimensional_time<D>::value)
> {};

template <typename T, typename D>
typename boost::enable_if<
  is_proper_TD_types< T, D >
, T >::type
operator+ (const T& t1, const D& d1)

Here, to determine the resulting function type, the compiler has to _instantiate_ (because enable_if uses it in a way that requires implicit instantiation) the class template is_proper_TD_types with T and D. The instantiation can fail for myriad of reasons (in this case because of using int in a qualified name), and its failure for any reason is not considered a type deduction failure, hence no SFINAE.

3. Second attempt at factoring the condition out for it to be reused - specifying the condition as a default argument for template parameter.

template <typename T, typename D,
              typename Enable = std::tr1::integral_constant<bool,
                                              is_Universal_time_API<T>::value &&
                                              (std::tr1::is_same<typename T::duration_t,D>::value ||
                                              std::tr1::is_same<duration,D>::value ||
                                              std::tr1::is_same<linear_duration,D>::value ||
                                              is_dimensional_time<D>::value) > >
struct is_proper_TD_types : Enable {};

template <typename T, typename D>
typename boost::enable_if<
  is_proper_TD_types< T, D >
, T >::type
operator+ (const T& t1, const D& d1)

Here if T is int, the function return type would be invalid because is_proper_TD_types< T, D > would be an invalid type itself (no instantiation of it involved!), as it will be is_proper_TD_types<
T, D, std::tr1::integral_constant<bool,
                                              is_Universal_time_API<T>::value &&
                                              (std::tr1::is_same<typename T::duration_t,D>::value ||
                                              std::tr1::is_same<duration,D>::value ||
                                              std::tr1::is_same<linear_duration,D>::value ||

                                              is_dimensional_time<D>::value) > >

, which is an invalid type because of attempted typename int::duration_t, and we have SFINAE.

Hope that I managed to carry my reasoning well enough, instead of creating even more confusion :)
>
> BTW, has my
> suggestion in the thread "Choosing a variable size" worked
> for
> you?

> Nothing has "worked", since it turns out that I have to worry about
> the range of the intermediate result being beyond what the final thing will
> hold, so might need to be larger. It would be great to get the whole
> function down to template metaprogramming, but that won't happen any time
> soon. For now, I'll look at optimizing particular cases if/when it turns
> out to be an issue on 32-bit builds, and my real problem (with just using the
> largest size) is dealing with signed vs. unsigned for that largest
> integer.

OK

Best Regards,
Gevorg


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net