Prev: It would be better if unique_ptr were a type modifier?
Next: Is there any standard/guarantees for exception safety in STL operations?
From: Kirill_Lykov on 26 Jul 2010 02:45 On Jul 26, 6:56 am, Daniel Krügler <daniel.krueg...(a)googlemail.com> wrote: > On 25 Jul., 17:18, Rodolfo Lima <rodo...(a)rodsoft.org> wrote: > > > > > Hi, I'm having some problems with the following code, gcc-4.5 refuses > > to compile it: > > > #include <iostream> > > > template <bool B, class V=void> struct enable_if {}; > > template <class V> struct enable_if<true,V> { typedef V type; }; > > > template <class T> > > struct bar {}; > > > template <class T, int ID, class V=void> > > struct foo; > > > template<template<class> class V, class T, int ID> > > struct foo<V<T>, ID> > > { > > static const int value = 1; > > }; > > > template<class T, int ID> > > struct foo<bar<T>,ID, typename enable_if<ID!=0>::type> > > { > > static const int value = 2; > > }; > > > int main() > > { > > std::cout << foo<bar<int>,1>::value << std::endl; > > } > > > //------------------------------------------ > > > The error message: > > teste.cpp: In function 'int main()': > > teste.cpp:26:30: error: ambiguous class template instantiation for > > 'struct foo<bar<int>, 1>' > > teste.cpp:14:1: error: candidates are: struct foo<V<T>, ID> > > teste.cpp:20:1: error: struct foo<bar<T>, ID, typename > > enable_if<(ID != 0)>::type> > > teste.cpp:26:15: error: incomplete type 'foo<bar<int>, 1>' used in > > nested name specifier > > > According to partial ordering rules, struct foo<bar<T>,ID, typename > > enable_if<ID!=0>::type> is more specialized than struct foo<V<T>, ID>, > > isn't it? So it should be used, instead of giving an ambiguity error. > > > Am I wrong or is the compiler? > > This time it's you ;-) > > The problem is that in the attempt to define the partial > specialization > > template<class T, int ID> > struct foo<bar<T>,ID, typename enable_if<ID!=0>::type>; > > you are running into a non-deducible context for the last > argument. Unfortunately the standard does not require this > attempt to be ill-formed. There is a core issue concerning > this situation, see: > > http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#549 > > HTH & Greetings from Bremen, > > Daniel Krügler > Сould answer one more question about this code? This code with some changes* may be compiled with VS 2010, but If I change the default type in the enable_if from void to int, it will cause a error: "use of undefined type 'foo<T,ID>' with [ T=bar<int>, ID=1 ]" However the code below works as I expect (according with 14.2.10). template <bool B, class V = int> struct enable_if { }; template <class V> struct enable_if<true, V> { typedef V type; }; auto v = enable_if<8!=0>(); // V == int * to make it work I changed the second template foo specialization this way: template<class T, int ID, template<class> class V> struct foo<T, ID, V<T>> ... -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Johannes Schaub (litb) on 26 Jul 2010 02:48
Daniel Krügler wrote: > On 26 Jul., 01:56, Daniel Krügler <daniel.krueg...(a)googlemail.com> > wrote: >> On 25 Jul., 17:18, Rodolfo Lima <rodo...(a)rodsoft.org> wrote: >> >> >> >> > Hi, I'm having some problems with the following code, gcc-4.5 refuses >> > to compile it: >> >> > #include <iostream> >> >> > template <bool B, class V=void> struct enable_if {}; >> > template <class V> struct enable_if<true,V> { typedef V type; }; >> >> > template <class T> >> > struct bar {}; >> >> > template <class T, int ID, class V=void> >> > struct foo; >> >> > template<template<class> class V, class T, int ID> >> > struct foo<V<T>, ID> >> > { >> > static const int value = 1; >> > }; >> >> > template<class T, int ID> >> > struct foo<bar<T>,ID, typename enable_if<ID!=0>::type> >> > { >> > static const int value = 2; >> > }; >> >> > int main() >> > { >> > std::cout << foo<bar<int>,1>::value << std::endl; >> > } >> >> > //------------------------------------------ >> >> > The error message: >> > teste.cpp: In function 'int main()': >> > teste.cpp:26:30: error: ambiguous class template instantiation for >> > 'struct foo<bar<int>, 1>' >> > teste.cpp:14:1: error: candidates are: struct foo<V<T>, ID> >> > teste.cpp:20:1: error: struct foo<bar<T>, ID, typename >> > enable_if<(ID != 0)>::type> >> > teste.cpp:26:15: error: incomplete type 'foo<bar<int>, 1>' used in >> > nested name specifier >> >> > According to partial ordering rules, struct foo<bar<T>,ID, typename >> > enable_if<ID!=0>::type> is more specialized than struct foo<V<T>, ID>, >> > isn't it? So it should be used, instead of giving an ambiguity error. >> >> > Am I wrong or is the compiler? >> >> This time it's you ;-) >> >> The problem is that in the attempt to define the partial >> specialization >> >> template<class T, int ID> >> struct foo<bar<T>,ID, typename enable_if<ID!=0>::type>; >> >> you are running into a non-deducible context for the last >> argument. Unfortunately the standard does not require this >> attempt to be ill-formed. There is a core issue concerning >> this situation, see: >> >> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#549 > > I was a bit too quick here: My analysis was wrong, because > ID has already been deduced. So let's declare the two test > overloads to verify partial ordering: > > template<template<class> class V, class T, int ID> > void test(foo<V<T>, ID>); > > template<class T, int ID> > void test(foo<bar<T>,ID, typename enable_if<ID!=0>::type>); > > I agree that the ambiguity is astonishing. I have the feeling you > are running into core issue > > http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#455 > > which describes problems with partial ordering in the presence > of non-deduced arguments, but I'm not 100% sure. > Indeed, I think the problem that guy describes in that issue report is similar to the problem we have here. I've analyzed the first case of the issue report, and i come to the same conclusion as comeau: The code seems to be valid: void(T, typename A<T>::type) // T1 void(T*, typename A<T>::type*) // T2 void(Tx, A<Tx>::type) // XT1 void(Tx*, A<Tx>::type*) // XT2 XT1 -> T2 void(Tx, A<Tx>::type) void(T*, typename A<T>::type*) => T* does not match Tx, second param non-deduced context XT2 -> T1 void(Tx*, A<Tx>::type*) void(T, typename A<T>::type) => T = Tx*, second param non-deduced context XT2 -> T1 OK, T2 at least as specialized as T1 XT1 -> T2 FAIL => T2 more specialized than T1. Clang and GCC don't seem to do partial ordering in the first place, i think. There is a difference between these two sets of compiler behaviors with regard to handling non-deduced contexts during argument deduction itself - i don't believe the difference is due to partial ordering. I have done two examples: template<typename T> struct id { typedef T *type; }; template<typename T> void f(T, typename id<T>::type); int main() { f(0, 0); } And template<typename T> struct id { typedef T type; }; template<typename T> void f(T, typename id<T>::type*); int main() { f(0, 0); } The only difference is the placement of the star. Comeau accepts both examples, because in both cases "int" is not compared to the non-deduced context, which i think is the correct behavior. GCC and Clang however only accept the first, for reasons unknown to me. They seem to think that a pointer cannot possibly match an int and reject, maybe thinking that this "dependent-type *" is a compound type, and they are allowed to do so (paragraph 6). I don't think that's correct though. The allowance of the Standard for compound types to contain both deduced and non-deduced contexts does not seem to apply to a ptr-declarator being still allowed to be checked - the phrase "compound types" seems to refer to the list in 14.9.2.5[temp.deduct.type]/3 - not to the general one in 3[basic] (and then interpreting a star piece as a "type" is still quite a stretch). There are two cases that i know of that can prevent comparison: - An argument that contains only explicitly specified arguments/contains no deduced arguments (14.9.1[temp.arg.explicit]/6) - Nondeduced context (14.9.2.5[temp.deduct.type]/4) The first is handled consistently also by Clang and GCC: template<typename T> void f(T); int main() { f<int*>(0); } -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |