
I'm using the Microsoft 2008 compiler.
The template
template
{};
gives an error "'T': must be a class or namespace when followed by '::'" when T is int. This kind of thing is mentioned in the enable_if documentation as the motivation for the "lazy" forms, because the compiler doesn't apply SFINAE for other templates.
But, riddle me this: I created that line as an attempt to refactor the following, which DOES work OK:
template

SFINAE principle applies only when overloading function templates. For a templated class/struct substitution must succeed.
So why does the bottom one compile? That is the question. TradeStation Group, Inc. is a publicly-traded holding company (NASDAQ GS: TRAD) of three operating subsidiaries, TradeStation Securities, Inc. (Member NYSE, FINRA, SIPC and NFA), TradeStation Technologies, Inc., a trading software and subscription company, and TradeStation Europe Limited, a United Kingdom, FSA-authorized introducing brokerage firm. None of these companies provides trading or investment advice, recommendations or endorsements of any kind. The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any review, retransmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited. If you received this in error, please contact the sender and delete the material from any computer.

Hi John, John Dlugosz wrote:
I'm using the Microsoft 2008 compiler.
The template
template
struct is_proper_TD_types : std::tr1::integral_constant
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) {};
gives an error "'T': must be a class or namespace when followed by '::'" when T is int.
Move the integral constant type you are deriving from to the template parameter list for SFINAE to apply. In this case that'll be:
template

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.
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. --John TradeStation Group, Inc. is a publicly-traded holding company (NASDAQ GS: TRAD) of three operating subsidiaries, TradeStation Securities, Inc. (Member NYSE, FINRA, SIPC and NFA), TradeStation Technologies, Inc., a trading software and subscription company, and TradeStation Europe Limited, a United Kingdom, FSA-authorized introducing brokerage firm. None of these companies provides trading or investment advice, recommendations or endorsements of any kind. The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any review, retransmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited. If you received this in error, please contact the sender and delete the material from any computer.

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
{};
template
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

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.
Hmm, I still think that the template expansion of cases 1 and 2 would "choke" in the same way. But the difference, I think, must be one of indirection: Case 1, the template instantiation of enable_if fails while directly seeing if it forms a usable return type. In Case 2, it fails as a nested invocation, a type needed by the (possible) instantiation of the return type of the function. As for the difference between 2 and 3, "once the class is known, do individual members including nested types make sense"? seems to be more arbitrary, like the compiler takes some things but not others. I don't see how that difference is explained in the standard. Thanks for your detailed analysis. --John (watch out for that footer!) TradeStation Group, Inc. is a publicly-traded holding company (NASDAQ GS: TRAD) of three operating subsidiaries, TradeStation Securities, Inc. (Member NYSE, FINRA, SIPC and NFA), TradeStation Technologies, Inc., a trading software and subscription company, and TradeStation Europe Limited, a United Kingdom, FSA-authorized introducing brokerage firm. None of these companies provides trading or investment advice, recommendations or endorsements of any kind. The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any review, retransmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited. If you received this in error, please contact the sender and delete the material from any computer.

John Dlugosz wrote:
Hmm, I still think that the template expansion of cases 1 and 2 would "choke" in the same way. But the difference, I think, must be one of indirection: Case 1, the template instantiation of enable_if fails while directly seeing if it forms a usable return type. In Case 2, it fails as a nested invocation, a type needed by the (possible) instantiation of the return type of the function.
As for the difference between 2 and 3, "once the class is known, do individual members including nested types make sense"? seems to be more arbitrary, like the compiler takes some things but not others. I don't see how that difference is explained in the standard.
Thanks for your detailed analysis.
--John
C++03 14.8.2/2 explicitly lists a finite set of kinds of errors which cause deduction failure and thus SFINAE, but it is not clear on the contexts in which those kind of errors cause deduction failure instead of a hard error. As a template class instantiation can fail due to far more kinds of errors (not only invalid types and invalid expressions, but also invalid member declarations, invalid base classes etc.) than those listed as causing type deduction failure, it can be assumed that all kinds of errors occurring while instantiating template classes cause hard errors, as it probably would be surprising if some errors in that context cause deduction failures and others hard errors. Consider the following example: template < typename T > struct A { typedef T type; typename T::value_type value; /*if T is double, attempting to use a type that is not a class type in a qualified name, which is a kind of error included in the set of those causing deduction failure*/ }; template < typename T > struct B { typedef T type; static const T value = T(1); /*if T is double, attempting to supply an initializer for static data member that is not of integral or enumeration type, which is NOT a kind of error included in the set of those causing deduction failure*/ }; template < typename T > struct C : T {/*if T is double, attempting to inherit from non-class type, which is NOT a kind of error included in the set of those causing deduction failure*/ typedef T type; }; template < typename T > typename A< T >::type f( T ); void f( ... ); template < typename T > typename B< T >::type g( T ); void g( ... ); template < typename T > typename C< T >::type h( T ); void h( ... ); int main() { f( 5.0 );// SFINAE or hard error? g( 6.0 );// SFINAE or hard error? h( 7.0 );// SFINAE or hard error? } I think it would not be consistent if the call to f() here resulted in SFINAE but the calls to g() and h() produced hard errors instead, so that's why I assume C++03 did not intend errors in external template instantiations to result in SFINAE. Fortunately, based on http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html, C++1x makes that explicit, as it (latest draft n3090/FCD) adds the following lines to the relevant paragraph: "Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed.—end note ]" Hope this helps, Gevorg

-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users- bounces@lists.boost.org] On Behalf Of Gevorg Voskanyan Sent: Tuesday, April 20, 2010 1:23 PM To: boost-users@lists.boost.org Subject: Re: [Boost-users] puzzle with enable_if
"Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and
OK, so "refactoring" in general won't work. The SFINAE must be directly caused by the template specialization mentioned in the function declaration. Your case f (struct A contains T::value_type) is a failure of A to specialize. My example where the function returned something derived from A, say struct A2 : A { ..., is not a failure of A2 but of a type needed by it. T::value_type is still a dependant type, I would have thought, but I see that the error is the use of T:: that is a part of A's syntax. The difference is a nested call to have a template instantiated. Is that the right track? Thanks, --John (footer woes...) TradeStation Group, Inc. is a publicly-traded holding company (NASDAQ GS: TRAD) of three operating subsidiaries, TradeStation Securities, Inc. (Member NYSE, FINRA, SIPC and NFA), TradeStation Technologies, Inc., a trading software and subscription company, and TradeStation Europe Limited, a United Kingdom, FSA-authorized introducing brokerage firm. None of these companies provides trading or investment advice, recommendations or endorsements of any kind. The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any review, retransmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited. If you received this in error, please contact the sender and delete the material from any computer.

John Dlugosz wrote:
OK, so "refactoring" in general won't work.
It will, at least I've yet to encounter a case which can't be refactored properly. Some special care must be taken in some cases though, like in the one in your original post.
The SFINAE must be directly caused by the template specialization mentioned in the function declaration.
The attempted template specialization shall be an invalid type itself (before the compiler can even consider to "instantiate" it) for it to cause SFINAE. Any errors that are delayed until the instantiation (such as an invalid base class type, as in your OP) will cause hard errors and not SFINAE as they are not in the "immediate context".
Your case f (struct A contains T::value_type) is a failure of A to specialize.
It is a failure of A to *instantiate* with T=double. A< double > specialization is an otherwise valid type, as opposed to e.g. A< double::value_type > which is not.
My example where the function returned something derived from A, say struct A2 : A { ..., is not a failure of A2 but of a type needed by it.
This is no different from the case above. It is still a failure of A2 to instantiate. The fact that the error is in a type needed by A2 doesn't matter here as the base class' type is needed at A2's instantiation time and no sooner, and A2's specialization was a valid type by itself, so the error caused by base class type being invalid happens at A2's instantiation time when it is too late for SFINAE and the result is a hard error.
T::value_type is still a dependant type, I would have thought, but I see that the error is the use of T:: that is a part of A's syntax. The difference is a nested call to have a template instantiated.
Trying to summarize, if you want erroneous template parameter substitutions to result in SFINAE and not hard error, you should define the metafunctions you use in a function declaration in a way, such that if the specialization is a valid type by itself, its instantiation will always succeed. The trick to make the dependent types and expressions used in the template definition as default arguments for newly introduced template parameters often comes handy for doing that, as I've shown earlier in this thread.
Is that the right track?
It seems pretty close. I hope you enjoyed this journey ;) Best Regards, Gevorg
participants (3)
-
Gevorg Voskanyan
-
John Dlugosz
-
Juraj Ivančić