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