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 boost::enable_if_c<
is_Universal_time_API<T>::value &&
(std::tr1::is_same::value ||
std::tr1::is_same::value ||
std::tr1::is_same::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
struct is_proper_TD_types : std::tr1::integral_constant::value ||
std::tr1::is_same::value ||
std::tr1::is_same::value ||
is_dimensional_time<D>::value)
{};
template
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 ::value ||
std::tr1::is_same::value ||
std::tr1::is_same::value ||
is_dimensional_time<D>::value) > >
struct is_proper_TD_types : Enable {};
template
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::value ||
std::tr1::is_same::value ||
std::tr1::is_same::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