in [C++]

Prev: ANNOUNCE: V1.4 (FCD edition) of just::thread C++0x thread library released
Next: c++0x perfect forwarding (again)
From: Mikosz on 7 May 2010 03:15 Hi all, I've been wondering for a couple of days now - why won't the following code compile (fails on both gcc and comeau online): struct X { int t; template <typename T> struct Y { T t; }; }; template <typename T1, typename T2> struct A { }; template<typename X, typename T> struct A< X, typename X::template Y<T> > { typedef int Type; }; int main() { typedef X::Y<int> YI; A<X, YI>::Type t; // <- COMPILATION ERROR return 0; } The compilation error says that A::Type is undefined. Now, if you change the code, so that both versions of A contain a constant of the same name, but with different value and you'll print it out in main(), you'll find that the code does compile, but the unspecialised template version is chosen. Since the templates compile just fine, it means that the specialised version is indeed a proper specialisation and is more detailed than the unspecialised one. How do I make the compiler select the specialised version then? If the construction is invalid and it's impossible to use the specialised version - why does the code compile at all? C++ gurus - what say you? :) Cheers, Mikolaj -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Andy Venikov on 8 May 2010 02:08 Mikosz wrote: > Hi all, > > I've been wondering for a couple of days now - why won't the following > code compile (fails on both gcc and comeau online): > > struct X > { > int t; > template <typename T> > struct Y > { > T t; > }; > }; > > > template <typename T1, typename T2> > struct A > { > }; > > template<typename X, typename T> > struct A< X, typename X::template Y<T> > > { > typedef int Type; > }; > > > int main() > { > typedef X::Y<int> YI; > A<X, YI>::Type t; // <- COMPILATION ERROR > return 0; > } > > The compilation error says that A::Type is undefined. Now, if you > change the code, so that both versions of A contain a constant of the > same name, but with different value and you'll print it out in main(), > you'll find that the code does compile, but the unspecialised template > version is chosen. > > Since the templates compile just fine, it means that the specialised > version is indeed a proper specialisation and is more detailed than > the unspecialised one. How do I make the compiler select the > specialised version then? If the construction is invalid and it's > impossible to use the specialised version - why does the code compile > at all? > > C++ gurus - what say you? :) > > Cheers, > Mikolaj > The problem with this code is that your specialization of A uses a non-deduced context and hence never gets instantiated. If your goal is to specialize template A on types that have a template struct Y, then you can achieve it this way: template <typename T, typename Enable = void> struct A { }; template <typename T> struct type2void { typedef void type; }; template <typename X> struct A<X, typename type2void<typename X::template Y<int> >::type > { typedef int Type; }; //then in your main: A<X>::Type t; //works fine. The idea is to disable all instantiations of A whith type T where T::template<int> does not exist. HTH, Andy. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Paul Bibbings on 8 May 2010 02:11 Mikosz <mikoszrrr (a)gmail.com> writes:> Hi all, > > I've been wondering for a couple of days now - why won't the following > code compile (fails on both gcc and comeau online): > > struct X > { > int t; > template <typename T> > struct Y > { > T t; > }; > }; > > > template <typename T1, typename T2> > struct A > { > }; > > template<typename X, typename T> > struct A< X, typename X::template Y<T> > > { > typedef int Type; > }; > > > int main() > { > typedef X::Y<int> YI; > A<X, YI>::Type t; // <- COMPILATION ERROR > return 0; > } In your specialization of struct A you may have one-two-many template parameters, depending on what your intentions are. If you make this: template< typename T > struct A< X, typename X::template Y< T > > { typedef int Type; } then compilation succeeds. Because of the commonality of names, you might be thinking that your version of the specialization is specialized on your struct X, but of course it is not. Having provided the first of your template parameters and called it X does not gain you anything over having defined this as: template< typename Foo, typename T > struct A< Foo, typename Foo::template Y< T > > { typedef int Type; } The question then remains why /this/ is not considered a better match over the primary template for A< X, YI >, and it would require me to pay close attention to the details of a number of subclauses of the standard in order to provide the appropriate references to answer that one, which I'm not able to do at the moment. Of course, if you /are/ aware of the exact form of your specialization and are asking your question in the knowledge of that, then nothing said here progresses you any further beyond what you already know! :-) Regards Paul Bibbings -- [ 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 8 May 2010 02:10 Mikosz wrote: > Hi all, > > I've been wondering for a couple of days now - why won't the following > code compile (fails on both gcc and comeau online): > > struct X > { > int t; > template <typename T> > struct Y > { > T t; > }; > }; > > > template <typename T1, typename T2> > struct A > { > }; > > template<typename X, typename T> > struct A< X, typename X::template Y<T> > > { > typedef int Type; > }; > > > int main() > { > typedef X::Y<int> YI; > A<X, YI>::Type t; // <- COMPILATION ERROR > return 0; > } > > The compilation error says that A::Type is undefined. Yes. The issue here is that `typeame X::template Y<T>` is a nondeduced context for both X and T. See 14.8.2.4/4 for what a non-deduced context is. "T" can't be deduced unless X is known, which itself can't be deduced using that parameter. After deduction is done, X is substituted there, but "T" is still left undeduced, and a deduction failure occurs. "If a template parameter is used only in nondeduced contexts and is not explicitly specified, template argument deduction fails." > Now, if you > change the code, so that both versions of A contain a constant of the > same name, but with different value and you'll print it out in main(), > you'll find that the code does compile, but the unspecialised template > version is chosen. > > Since the templates compile just fine, it means that the specialised > version is indeed a proper specialisation and is more detailed than > the unspecialised one. How do I make the compiler select the > specialised version then? If the construction is invalid and it's > impossible to use the specialised version - why does the code compile > at all? > It is a proper specialization that is never taken because it never "matches" the argument list. See 14.5.4.1/2. You can deduce T as a whole, and then check whether it's a specialization of a member-template of X: struct X { int t; template <typename T> struct Y { T t; }; }; template <typename T1, typename T2, typename = void> struct A { }; template<typename X> struct hasit { template<typename T> struct check { }; /* notice: X is known, doesn't need deduction for check! */ template<typename T> struct check< typename X::template Y<T> > { typedef void does; typedef T arg1_type; }; }; template<typename X, typename T> struct A< X, T, typename hasit<X>::template check<T>::does > { typedef int Type; }; Now, because the outer X is already known, you are one step ahead in deduction, and can immediately compare both Y<T>'s for compatibility and deduce T right away. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: red floyd on 8 May 2010 02:12
On May 7, 11:15 am, Mikosz <mikosz... (a)gmail.com> wrote:> Hi all, > > I've been wondering for a couple of days now - why won't the following > code compile (fails on both gcc and comeau online): > > struct X > { > int t; > template <typename T> > struct Y > { > T t; > }; > > }; > > template <typename T1, typename T2> > struct A > { > > }; > > template<typename X, typename T> > struct A< X, typename X::template Y<T> > > { > typedef int Type; > > }; > > int main() > { > typedef X::Y<int> YI; > A<X, YI>::Type t; // <- COMPILATION ERROR > return 0; > > } > It's a dependent name. You need typename A<X, YI>::Type t; See FAQ 35.18 http://parashift.com/c++-faq-lite/templates.html#faq-35.18 -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |