If a type is specified as:
A<T>::B<T2>,
both T and T2 are nondeduced.
I think an example workaround is at
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=13088
in the 2004-02-28 21:25 attachment. It involves
"parsing" the A<T>::B<T2> with template specializations
until the A<T> become in a deduced context.
Does anyone have a better method or has this already
been discovered? I'm guessing that this must be
what the standard writers had in mind when they
come up with this nondeduced context restriction, because
I really needed this and it seemed an unneeded
restriction. Does anybody have any more certain
idea about the rationale behind nondeduced contexts?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
> The standard, in 14.8.2.4p4 says:
>
> If a type is specified as:
> A<T>::B<T2>,
> both T and T2 are nondeduced.
>
> I think an example workaround is at
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=13088
> in the 2004-02-28 21:25 attachment. It involves
> "parsing" the A<T>::B<T2> with template specializations
> until the A<T> become in a deduced context.
>
> Does anyone have a better method or has this already
> been discovered? I'm guessing that this must be
> what the standard writers had in mind when they
> come up with this nondeduced context restriction, because
> I really needed this and it seemed an unneeded
> restriction. Does anybody have any more certain
> idea about the rationale behind nondeduced contexts?
Surely you can see why T must be nondeduced in this case?
struct base {
template <class T2> struct B {};
};
template <class T>
struct A : base {};
I'm rather surprised that once T2 is given explicitly, T2 is still
nondeduced, though. AFAICT, once we know what A<T>::B is referring
to, it's reduced to the same problem as deducing T in
template <class T> struct X {};
template <class T> void f(X<T>);
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
This merely seems like a defect since there is no real computational
limitation here and it can be easily workaround:
template<typename T>
struct A {
template<typename U> struct B {};
};
template<typename T, typename U>
int f(typename A<T>::template B<U>);
typedef A<int>::B<char> type;
//int x = f<int>(type()); // #1 - problematic case
// workaround using additional class for the non-deuced parameter
template<typename T>
struct X
{
typedef A<T> outer_type;
template<typename U>
static int f(typename outer_type::template B<U>);
};
int x1 = X<int>::f(type()); // #2
Comeau C/C++ 4.3.3 had no problems with #1 and other compliers that I tried
(VC6+, BCC5.6 and GCC) had no problems with #2.
> I'm guessing that this must be
> what the standard writers had in mind when they
> come up with this nondeduced context restriction, because
> I really needed this and it seemed an unneeded
> restriction. Does anybody have any more certain
> idea about the rationale behind nondeduced contexts?
In general, restrictions related to non-deduced contexts might seem
unjustified but such restrictions are actually rooted from fundamental
limitations:
Rani
http://gcc.gnu.org/bugzilla/attachment.cgi?id=5821&action=view
is that the nested template class (B above and owner in bugzilla)
is in a super class (base) instead of in the immediate class
(A above and curry_visitor_referent in bugzilla). But surely
that shouldn't make a difference, or does it?
>
> I'm rather surprised that once T2 is given explicitly, T2 is still
> nondeduced, though. AFAICT, once we know what A<T>::B is referring
> to, it's reduced to the same problem as deducing T in
>
> template <class T> struct X {};
> template <class T> void f(X<T>);
>
But the problem I was experiencing is not quite like that. I'm
explicitly specifying the arguments to the class templates
and referencing a static function. The output for the bugzilla code is:
test_nondeduced<curry_visitor_referent<A>::owner<B*> >::rc=1
test_yesdeduced<curry_visitor_referent<A>::owner<B*> >::rc=0
It's easy for me to deduce from:
test_nondeduced<curry_visitor_referent<A>::owner<B*> >
that:
Visitor=A
Referent=B
for the specialization:
template
< typename Visitor
, typename Referent
>
class
test_nondeduced
< curry_visitor_referent<Visitor>::owner<Referent*>
>
;
so it's hard for me to figure why the compiler can't do it.
What am I missing?
however, John Spicer was kind enough to test it with a new
version of edg it that version gives several errors. Hence,
my workaround doesn't work :(
I've just glanced at Rani's response and will see if it
will work.
Thanks for any further help.
| On 02/29/2004 11:27 AM, David Abrahams wrote:
| > Larry Evans <cpplj...@cox-internet.com> writes:
| [snip[
| > Surely you can see why T must be nondeduced in this case?
| ^the following case?
| >
| > struct base {
| > template <class T2> struct B {};
| > };
| >
| > template <class T>
| > struct A : base {};
| Not yet :( . The only difference between the above code
| and that in the bugzilla attachment:
|
| http://gcc.gnu.org/bugzilla/attachment.cgi?id=5821&action=view
^^^^
I suppose you meant "13088", for "5821" is about some obscure macro games.
I think, it basically boils down to the fact that a C++ compiler is
simply a piece of software with no particular "thinking device" built
in :-)
| What am I missing?
I did not look at that PR before, but now that you rephrase your
problem I recall having seen a similar case in the past, brought the
issue to the Core Working Group, and came back with answer that the
"standard is correct (and clear) as written" ;-)
Basically, here is what is going on. *Whenever*, you have a partial
specialization -- no matter whether the partial specialization set is
a singleton or not -- you *potentially* run the risk of going through
a template-argument deduction process (that process does not
necessarily happen at the definition time of the partial
specialization). The relevant rules are:
14.5.4.1 Matching of class template partial specializations
1 When a class template is used in a context that requires an
instantiation of the class, it is necessary to determine whether the
instantiation is to be generated using the primary template or one
of the partial specializations. This is done by matching the
template arguments of the class template specialization with the
template argument lists of the partial specializations.
-- If exactly one matching specialization is found, the
instantiation is generated from that specialization.
-- If more than one matching specialization is found, the partial
order rules (14.5.4.2) are used to determine whether one of the
specializations is more specialized than the others. If none of
the specializations is more specialized than all of the other
matching specializations, then the use of the class template is
ambiguous and the program is ill-formed.
-- If no matches are found, the instantiation is generated from
the primary template.
At this point, you might have thought that if the set of partial
specializations is a singleton, then the job is done... right? Wrong.
The key word is "matching". Yup. The standard text goes on saying:
2 A partial specialization matches a given actual template argument
list if the template arguments of the partial specialization can be
deduced from the actual template argument list (14.8.2).
So, in order to decide whether your partial specialization matches the
given template argument(s), you have to go through the template
argument deduction process to see whether you can deduce values for
the template-parameters of the partial specialization, and if so
whether those values are equivalent to the template-arguments. In your
specific case under discussion, both template-parameters appear in
non deducible contexts, therefore they cannot be deduced, therefore
your partial specialization does not match! Yes, it does not -- even
if you can tell by "looking". The moral is:
when writing a partial specialization, be sure to put your
template-parameters in deducible contexts. Or else, bad things may
happen.
Note: I'm not saying the Standard is right. I'm merely explaining why
your compiler behaves the way it does.
--
Gabriel Dos Reis
g...@cs.tamu.edu
Texas A&M University -- Computer Science Department
301, Bright Building -- College Station, TX 77843-3112
> Larry Evans wrote:
>
[snip of c++standard citation]
> This merely seems like a defect since there is no real computational
> limitation here and it can be easily workaround:
>
[snip of workaround]
Actually, I don't think this'll work for what I want. What I want is
a templated class, cycle_mgr<O> used as a superclass of another
templated class resrc_mgr<T,O>. I want cycle_mgr to be specialized on
cycle_mgr<curry_visitor_referent<A>::owner<B*> > for any A and B.
Could you show me how your workaround could do this?
>
>
> In general, restrictions related to non-deduced contexts might seem
> unjustified but such restrictions are actually rooted from fundamental
> limitations:
>
> http://tinyurl.com/2l8hz
>
Thanks. That helps. I did search for "nondeduced context" in
commp.lang.c++.moderated and in std.c++ and elsewhere, but got nothing
relevant :(
Regards,
Larry
> On 02/29/2004 11:27 AM, David Abrahams wrote:
>> Larry Evans <cpplj...@cox-internet.com> writes:
> [snip[
>> Surely you can see why T must be nondeduced in this case?
> ^the following case?
Yes.
>> struct base {
>> template <class T2> struct B {};
>> };
>>
>> template <class T>
>> struct A : base {};
A<int>::B<int> is base::B<int>
A<char*>::B<int> is base::B<int>
A<short>::B<int> is base::B<int>
A<T>::B<int> is base::B<int>
...
Since these are all the same type, I don't see how you can deduce
what T is. If you can't do it, neither can the compiler.
This is a similar problem to deducing T in this function template:
template <class T>
void f(typename T::type x);
Since multiple classes can have the same nested ::type, how can you
possibly choose one?
> Not yet :( . The only difference between the above code
> and that in the bugzilla attachment:
>
> http://gcc.gnu.org/bugzilla/attachment.cgi?id=5821&action=view
>
> is that the nested template class (B above and owner in bugzilla)
> is in a super class (base) instead of in the immediate class
> (A above and curry_visitor_referent in bugzilla). But surely
> that shouldn't make a difference, or does it?
I think my point is that it *doesn't* make a difference. The standard
decides deducibility based on the form of the function parameter, not
on the particular way the function argument type is put together.
Remember that a specialization of the argument class template could
come along later and change everything.
>> I'm rather surprised that once T2 is given explicitly, T2 is still
^^
Sorry, I meant T above.
>> nondeduced, though. AFAICT, once we know what A<T>::B is referring
>> to, it's reduced to the same problem as deducing T in
>>
>> template <class T> struct X {};
>> template <class T> void f(X<T>);
>>
> But the problem I was experiencing is not quite like that. I'm
> explicitly specifying the arguments to the class templates
> and referencing a static function. The output for the bugzilla code is:
>
> test_nondeduced<curry_visitor_referent<A>::owner<B*> >::rc=1
> test_yesdeduced<curry_visitor_referent<A>::owner<B*> >::rc=0
I looked at the code a bit but was unable to tell what it's supposed
to be doing or what the problem is. Can't you make a testcase that
fails to compile because of the problem in question?
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
I see your point now. However, my problem involves class template
specializations, not function template specializations. Sorry if that
wasn't clear. IIUC, the number of template parameters in the template
parameter list for a class template specialization does not have to be
the same as the number of template parameters for the primary class
template. So, if the primary is:
template
< typename OwnerReferent
>
class
test_nondeduced
;
then the specialization can be:
template
< typename Visitor
, typename Referent
>
class
test_nondeduced
< curry_visitor_referent<Visitor>::owner<Referent*>
>
;
there the parameter list for the primary is <typename OwnerReferent>
and for the specialization is <typename Visitor, typename Referent>,
where "template parameter list" is described in 14.5.4p5.
The only constraint is that, according to 14.5.4.1p4:
In a type name that refers to a class template specialization,
(e.g., A<int, int, 1>) the argument list must match the template
parameter list of the primary template. The template arguments of
a specialization are deduced from the arguments of the primary
template.
Here "argument list" is "template argument list" as defined by
14.5.4p5. Since curry_visitor_referent<Visitor>::owner<Referent*> is a
type, and the primary template has 1 type parameter
(i.e. OwnerReferent), this constraint is satisfied.
Now, since there are 2 template parameters in the partial
specialization, the actual instantiation by:
struct A{};structB{};
test_nondeducec<curry_visitor_referent<A>::owner<B*> >
should deduce Visitor=A and Referent=B.
[snip]
>>
>>But the problem I was experiencing is not quite like that. I'm
>>explicitly specifying the arguments to the class templates
>>and referencing a static function. The output for the bugzilla code is:
>>
>> test_nondeduced<curry_visitor_referent<A>::owner<B*> >::rc=1
>> test_yesdeduced<curry_visitor_referent<A>::owner<B*> >::rc=0
>
>
> I looked at the code a bit but was unable to tell what it's supposed
> to be doing or what the problem is.
I wanted rc=0. The nondeduced didn't do it because of the nondeduced
context, and I thought I had a workaround by decomposing the
argument. The about output is for intel 8.0, but John Spicer says that
compiler has a bug and he has a fix.
> Can't you make a testcase that
> fails to compile because of the problem in question?
John Spicer compiled the following with his fix:
<-- test.cpp ------
//Purpose:
// Test whether template metaprogramming can
// solve the non-deduced context problem with
// curry_prox_visitor_refcycle_counted.
#include <iostream>
template
< typename OwnerReferent
>
class
test_nondeduced
{
public:
static
int
rc(void)
{
return 1;
}
};
template
< typename Visitor
>
class
curry_visitor_referent
{
public:
template
< typename Referent
>
class
owner
{
};
};
#if 0
template
< typename Visitor
, typename Referent
>
class
test_nondeduced
< curry_visitor_referent<Visitor>::owner<Referent*>
>
{
public:
static
int
rc(void)
{
return 0;
}
};
#endif
template
< typename OwnerReferent
>
class
test_yesdeduced
{
public:
static
int
rc(void)
{
return 1;
}
};
template
< typename Outer
, typename Referent
>
class
outer_referent
;
template
< typename Visitor
, typename Referent
>
class
outer_referent
< curry_visitor_referent<Visitor>
, Referent
>
{
public:
static
int
rc(void)
{
return 0;
}
};
template
< template <typename>class Owner
, typename Referent
>
class
owner_referent
;
template
< typename Outer
, typename Referent
>
class
owner_referent
< Outer::template owner
, Referent
>
: public outer_referent<Outer,Referent>
{
};
template
< template <typename>class Owner
, typename Referent
>
class
test_yesdeduced
< Owner<Referent*>
>
: public owner_referent<Owner,Referent>
{
};
struct A{};
struct B{};
int
main(void)
{
typedef
test_nondeduced
< curry_visitor_referent<A>::owner<B*>
>
nondeduced_type;
typedef
test_yesdeduced
< curry_visitor_referent<A>::owner<B*>
>
yesdeduced_type;
std::cout<<"test_nondeduced<curry_visitor_referent<A>::owner<B*>
>::rc="<<nondeduced_type::rc()<<"\n";
std::cout<<"test_yesdeduced<curry_visitor_referent<A>::owner<B*>
>::rc="<<yesdeduced_type::rc()<<"\n";
return 0;
}
>------------------
and got the following errors:
<-- test.err ------
"t.c", line 110: error: template parameter "Outer" is not used in template
argument list of class template
"owner_referent<Outer::owner, Referent>"
< typename Outer
^
"t.c", line 130: error: incomplete type is not allowed
: public owner_referent<Owner,Referent>
^
detected during instantiation of class
"test_yesdeduced<Owner<Referent *>> [with
Owner=curry_visitor_referent<A>::owner, Referent=B]" at
line 151
"t.c", line 151: error: class
"test_yesdeduced<curry_visitor_referent<A>::owner<B *>>" has no
member "rc"
std::cout<<"test_yesdeduced<curry_visitor_referent<A>::owner<B*>
>::rc="<<yesdeduced_type::rc()<<"\n";
^
3 errors detected in the compilation of "t.c".
>------------------
"test_yesdeduced<uncurry_visitor_referent<A, B> >::rc()="
<<test_yesdeduced<uncurry_visitor_referent<A, B> >::rc()
<<"\n";
std::cout<<
"test_yesdeduced< curry_visitor_referent<A>::owner<B> >::rc()="
<<test_yesdeduced< curry_visitor_referent<A>::owner<B> >::rc()
<<"\n";
is:
test_yesdeduced<uncurry_visitor_referent<A, B> >::rc()=0
test_yesdeduced< curry_visitor_referent<A>::owner<B> >::rc()=1
So, I must be wrongly interpreting 14.8.2.1p2. Would anyone like to
clarify this paragraph for me? Please email if you'd like the code for
the above test.
> On 03/01/2004 01:29 PM, David Abrahams wrote:
> [snip]
> >
> >>> struct base {
> >>> template <class T2> struct B {};
> >>> };
> >>>
> >>> template <class T>
> >>> struct A : base {};
> >
> >
> > A<int>::B<int> is base::B<int>
> > A<char*>::B<int> is base::B<int>
> > A<short>::B<int> is base::B<int>
> > A<T>::B<int> is base::B<int>
> > ...
> >
> > Since these are all the same type, I don't see how you can deduce
> > what T is. If you can't do it, neither can the compiler.
>
> I see your point now. However, my problem involves class template
> specializations, not function template specializations. Sorry if that
> wasn't clear.
Doesn't matter; it's the same problem.
Sure.
> Now, since there are 2 template parameters in the partial
> specialization, the actual instantiation by:
>
> struct A{};structB{};
> test_nondeducec<curry_visitor_referent<A>::owner<B*> >
>
> should deduce Visitor=A and Referent=B.
No; it's the same problem I mentioned before. Any number of different
As can yield exactly the same type for
curry_visitor_referent<A>::owner<B*>. There's no way for the
compiler to deduce what A should be.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
OK. I FINALLY get it :} . The "only difference" I mentioned previously
between your code and mine was the "critical difference". It wouldn't
happen in my case since owner is nested inside curry_visitor_referent
unlike the B<T2> in your A<T>.
Thanks for your help and patience.
Actually, it will. Thanks to David Abrahams for opening my eyes.
What was confusing to me was the "extra class" X. If I had expressed
my problem more completely, I imagine the solution you proposed would
have been plainer to me. Anyway, the solution specific to my problem
is:
<---------------- source:cut here ----------------
#include <iostream>
struct a_vis{};
struct a_ref{};
struct no_nest
//Flag class indicating a class not nested
{
};
template
< typename Referent
>
struct
non_nested_owner
{
typedef
no_nest
nester
//indicate this class not nested within another
;
};
template
< typename Visitor
>
struct
curry_visitor_referent
{
template
< typename Referent
>
struct
owner
{
typedef
curry_visitor_referent<Visitor>
nester
//The class nesting this one.
;
};
};
template
< typename Nester
, typename Referent
>
struct
nester_referent
{
nester_referent(void)
{
std::cout<<"non-specialized nester_referent\n";
}
};
template
< typename Visitor
, typename Referent
>
struct
nester_referent
< curry_visitor_referent<Visitor>
, Referent
>
{
nester_referent(void)
{
std::cout<<"yes-specialized
nester_referent<curry_visitor_referent<Visitor> >\n";
}
};
template
< typename OwnerReferent
>
struct
nested_deduction
;
template
< template<typename>class OwnerTmpl
, typename Referent
>
struct
nested_deduction
< OwnerTmpl<Referent>
>
: public nester_referent
< typename OwnerTmpl<Referent>::nester
, Referent
>
{
};
int
main(void)
{
nested_deduction<non_nested_owner<a_ref> > non_nested;
nested_deduction<curry_visitor_referent<a_vis>::owner<a_ref> >
yes_nested;
return 0;
}
>--------------------end cut here ----------------
The output of which is:
<--------------- output: cut here ----------------
non-specialized nester_referent
yes-specialized nester_referent<curry_visitor_referent<Visitor> >
>--------------------end cut here ----------------
The
typedef A<T> outer_type;
in your struct X plays the same role as
typedef
curry_visitor_referent<Visitor>
nester
//The class nesting this one.
;
in curry_visitor_referent<Visitor>::owner<Referent>. I wasn't
understanding why X was used. Maybe I still don't but at
least I've got a near solution. The only other problem is that
this solution requires adding a nester typedef to each class
I expect to use as argument to nested_deduction.
Thanks for the help.
Although intel8.0 get's the result I wanted, the g++ from 2004/02/25
doesn't. Both outputs are included in the attachment. I believe
intel's is correct; however, if anybody thinks different, please let me
know. BTW, John Spicer tested the "vertical" version of the code
with his new version of edg and it also got the result I wanted.
I've started to use this solution in my code, and then I started
thinking, "this would be a lot of changes to existing template
classes, i.e. each class corresponding to non_nested_owner."
Then I thought the standard writers must have had a less
"intrusive" solution in mind. How can I adapt the
curry_visitor_refererent::owner class, without changing
exiting classes, to allow specialization in the nested_deduction
template class?
TIA.
http://www.semantics.org/once_weakly/w02_SFINAE.pdf
http://cvs.sourceforge.net/viewcvs.py/boost/boost/boost/mpl/aux_/has_xxx.hpp