[enable_if] usage in class specialization

Good day, a while ago I've posted a message with the title 'enable_if: problems with Intel C++ 10.0.023'. Although correct, the title was maybe too narrow to capture attention, but the problem seems to be much wider. In short, the documentation for enable_if shows that it can be used for selecting a class specialization depending on a predicate. Now Intel C++ refuses to do that and the frontend team at Intel believes this is the right thing to do (although they'll look into the issue further for the sake of compatibility with GCC). Their argument is that the SFINAE rule applies to the selection of the overload set for functions only and not to partial specialization for classes. I don't have my copy of the standard handy (I keep loosing the pdf and I've already paid my $18 twice...), so I cannot check this. Are there known workarounds? Best regards, Maurizio I'm including my original post for reference. ================== Original Post ====================== The following is distilled from a much larger code base and exibits a discrepancy between gcc 4.2.0 and the Intel C++ compiler 10.0.23 (both under Linux). GCC compiles the example fine, while the Intel compiler chokes on the last statement, objecting that: ------------------------------ Intel error message ------------------------------ -*- mode: compilation; default-directory: "~/dev/proto_pdl/" -*- Compilation started at Thu Jun 21 16:34:47 /opt/intel/cce/10.0.023/bin/icpc -I. -obe be.cpp be.cpp(15): error: class "boost::enable_if<boost::is_same<B, A>, void>" has no member "type" struct foo<T, typename boost::enable_if<boost::is_same<X,A> >::type> ^ detected during instantiation of class "S<X> [with X=B]" at line 24 be.cpp(15): error: class "boost::enable_if<boost::is_same<B, A>, void>" has no member "type" struct foo<T, typename boost::enable_if<boost::is_same<X,A> >::type> ^ detected during instantiation of class "S<X> [with X=B]" at line 24 compilation aborted for be.cpp (code 2) It seems like the very mechanism on which enable_if is based (SFINAE rule) makes the intel compiler unhappy. The release notes for version 10 have a fragment that is suspicious: New -early-template-check Switch Even though recent versions of g++ (3.4 and newer) parse template definitions, they do very little semantic checking of such definitions. Most of the semantic checking is delayed until an actual instantiation is done. As a result, g++ accepts certain unusable templates provided they are not actually used in the program. A new option is available (-early-template-check) to allow Intel C++ users to check the semantics of function template prototypes before instantiation. Example: class A {}; template <class T> struct B { B () {}; // error with -early-template-check): no initializer for // reference member "B<T>::a" A& a; }; Note that this switch will work in gcc 3.4 and later compatibility modes only (i.e. -gcc-version=340 and later). This leads me to believe that when not in gcc compatibility mode they try to be more agressive and analyze template code prematurely (btw, the code included doesn't compile even with -gcc-version=420). What is the general opinion on the validity of enable_if w.r.t to the standard? Are there known workarounds for the Intel compiler? Best regards, Maurizio ------------------------------ Code ------------------------------ #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_same.hpp> struct A {}; struct B {}; template<typename X> struct S { template<typename T, typename Enable=void> struct foo { enum { value=0 }; }; template<typename T> struct foo<T, typename boost::enable_if<boost::is_same<X,A> >::type> { enum { value=1 }; }; }; int main () { int i = S<A>::foo<B>::value; int j = S<B>::foo<B>::value; // OK for g++ 4.2.0, NOK for Intel C++ 10.0.23 }

Maurizio Vitale wrote:
In short, the documentation for enable_if shows that it can be used for selecting a class specialization depending on a predicate. Now Intel C++ refuses to do that and the frontend team at Intel believes this is the right thing to do (although they'll look into the issue further for the sake of compatibility with GCC). Their argument is that the SFINAE rule applies to the selection of the overload set for functions only and not to partial specialization for classes.
I think that the compiler is right to reject your code since the predicate doesn't depend on T. The S in SFINAE stands for 'substitution', and no substitution occurs in your predicate. The instantiation of S<B> can fail even without a reference to foo because your second declaration of foo is ill-formed.

On Jun 25, 2007, at 11:50 AM, Peter Dimov wrote:
Maurizio Vitale wrote:
In short, the documentation for enable_if shows that it can be used for selecting a class specialization depending on a predicate. Now Intel C++ refuses to do that and the frontend team at Intel believes this is the right thing to do (although they'll look into the issue further for the sake of compatibility with GCC). Their argument is that the SFINAE rule applies to the selection of the overload set for functions only and not to partial specialization for classes.
I think that the compiler is right to reject your code since the predicate doesn't depend on T. The S in SFINAE stands for 'substitution', and no substitution occurs in your predicate. The instantiation of S<B> can fail even without a reference to foo because your second declaration of foo is ill-formed.
Thanks this cleared the issue for me. The following goes through the Intel compiler as well. Instantiation is a bit more cumbersome (but not terrible because in my real code it would happen from within S), but at least is (hopefully) standard compliant. template<typename X> struct S { template<typename T, typename XX, typename Enable=void> struct foo { enum { value=0 }; }; template<typename T, typename XX> struct foo<T, XX, typename enable_if<is_same<XX,A> >::type> { enum { value=1 }; }; };
participants (3)
-
Maurizio Vitale
-
Maurizio Vitale
-
Peter Dimov