Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

unresolved function arguments with required typename

3 views
Skip to first unread message

Gary Kedziora

unread,
Jan 26, 2008, 2:26:58 PM1/26/08
to
Hi,

In the following code snippet, the argument to function 'fun' can not
be resolved. If I uncomment the int specialization just below, it
compiles, but if I don't use the specialized code, I get the following
error message:

error: no matching function for call to `fun(outer<int>::inner&)'

Why is this? Also, what is the best work around so that I can use a
generic functions similar to 'fun' with nested classes and templates.

Thanks,
Gary


++++#++++0++++#++++0++++#++++0++++#++++0++++#++++0++++#++++0
#include <iostream>
using namespace std;

template<class T> class outer {
public:
class inner {
T m;
public:
void set(T j) { m = j; }
void print(void) {cout << "m=" << m << endl;}
T get() { return m; }
};
};

// this doesn't work
template<class T> T fun ( typename outer<T>::inner &c )
{ return c.get(); }

// this works
//int fun ( outer<int>::inner &c )
// { return c.get(); }

int main() {
outer<int>::inner I;
I.set(3);
int flop = fun(I);
cout << "flop=" << flop << endl;
return 0;
}
++++#++++0++++#++++0++++#++++0++++#++++0++++#++++0++++#++++0

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Barry

unread,
Jan 27, 2008, 6:26:59 AM1/27/08
to
Gary Kedziora wrote:


> // this doesn't work
> template<class T> T fun ( typename outer<T>::inner &c )
> { return c.get(); }
>
> // this works
> //int fun ( outer<int>::inner &c )
> // { return c.get(); }
>
> int main() {
> outer<int>::inner I;
> I.set(3);
> int flop = fun(I);

int flop = fun<int>(I);

> cout << "flop=" << flop << endl;
> return 0;
> }
> ++++#++++0++++#++++0++++#++++0++++#++++0++++#++++0++++#++++0
>


the reason is that you can't do a "reverse deduction" for template
function argument, as the compiler don't know which specialization of
"outter" contains a nested class "inner".
this is like

20.2.2/1
template <class T> struct identity { typedef T type; };

20.2.2/2
[ Note: The use of identity in forward forces users to explicitly
specify the template parameter. This is necessary to get the correct
forwarding semantics. —end note ]

in the C++0x

--
Best Regards
Barry

Daniel Krügler

unread,
Jan 27, 2008, 6:21:13 AM1/27/08
to
On 26 Jan., 20:26, Gary Kedziora <gkedzi...@gmail.com> wrote:
> In the following code snippet, the argument to function 'fun' can not
> be resolved. If I uncomment the int specialization just below, it
> compiles, but if I don't use the specialized code, I get the following
> error message:
>
> error: no matching function for call to `fun(outer<int>::inner&)'
>
> Why is this? Also, what is the best work around so that I can use a
> generic functions similar to 'fun' with nested classes and templates.

This is actually a quite often occuring
problem and should be part of the FAQ
(with a short glance on it I couldn't
find it there).

First to the why:

> template<class T> class outer {
> public:
> class inner {
> T m;
> public:
> void set(T j) { m = j; }
> void print(void) {cout << "m=" << m << endl;}
> T get() { return m; }

I suggest usage of const-correct member functions.
In this case both print and get are reasonable
candidates for const functions. This is *not* the
source of your problem, but you should reconsider
your design in this regard.

> // this doesn't work
> template<class T> T fun ( typename outer<T>::inner &c )
> { return c.get(); }

The declaration of this function template is
language-conforming, but can never deduce T
out of a given outer<T>::inner argument (you
can only invoke it by providing explicit
template arguments, which is obviously not
your intend). The reason for this seemingly
surprising situation is the nature of templates
themselves: Since it is possible to specialize
them either partially or completely, each such
specialization is independent from the above
shown primary template. Consider the existence
of this specialization

template<> class outer<int> {
public:
typedef bool inner;
};

or this one

template<> class outer<double>{}; // no inner at all!

Now you should recognize that the compiler
can generally *not* backtrack from some
outer<T>::inner to T.

Note also that your workaround simply
works because it is no longer a template,
therefore the compiler needs only to
perform a simpler overload deduction.

Second to the solution: Probably the easiest
one is to add a *non-template* free friend
function to the outer template like this:

template<class T> class outer {
public:
class inner {

... // unchanged
};

friend T fun(inner &c )
{ return c.get(); }
};

This idiom is also known as "Barton-Nackman"
trick and has it's roots in a very old
rule of C++ from times where function templates
could not be overloaded. You should be aware
that fun here is *not* a member function of
outer, but just a free function, which will
only be taken into account via argument-depending
lookup (ADL).

HTH & Greetings from Bremen,

Daniel Krügler

Allen Ding

unread,
Jan 27, 2008, 6:26:04 AM1/27/08
to
> // this doesn't work
> template<class T> T fun ( typename outer<T>::inner &c )
> { return c.get(); }
>

Qualified type names are never used to deduce a template parameter.
In your case, outer<T>::inner will not be used to deduce the template
parameter T.

- Allen

Gary Kedziora

unread,
Jan 29, 2008, 4:04:41 PM1/29/08
to
On Jan 27, 6:26 am, Barry <dhb2...@gmail.com> wrote:
> Gary Kedziora wrote:
> > // this doesn't work
> > template<class T> T fun ( typename outer<T>::inner &c )
> > { return c.get(); }
>
> > // this works
> > //int fun ( outer<int>::inner &c )
> > // { return c.get(); }
>
> > int main() {
> > outer<int>::inner I;
> > I.set(3);
> > int flop = fun(I);
>
> int flop = fun<int>(I);
>

This little model code that I presented didn't express that I wanted
to use the binary multiplication operator, so a technique like the one
expressed on the line above wouldn't work.

> the reason is that you can't do a "reverse deduction" for template
> function argument, as the compiler don't know which specialization of
> "outter" contains a nested class "inner".

Oh, I thought all of them would contain inner parameterized by T.

> this is like
>
> 20.2.2/1
> template <class T> struct identity { typedef T type; };
>
> 20.2.2/2
> [ Note: The use of identity in forward forces users to explicitly
> specify the template parameter. This is necessary to get the correct

> forwarding semantics. --end note ]
>
> in the C++0x
>
> --

I looked this up. It is very intriguing. I'll have to study that
further.

Thanks!
Gary

--

Gary Kedziora

unread,
Jan 29, 2008, 8:19:57 PM1/29/08
to
On Jan 27, 6:21 am, "Daniel Krügler" <daniel.krueg...@googlemail.com>
wrote:

> On 26 Jan., 20:26, Gary Kedziora <gkedzi...@gmail.com> wrote:
>
> > In the following code snippet, the argument to function 'fun' can not
> > be resolved. If I uncomment the int specialization just below, it
> > compiles, but if I don't use the specialized code, I get the following
> > error message:
>
> > error: no matching function for call to `fun(outer<int>::inner&)'
>
> > Why is this? Also, what is the best work around so that I can use a
> > generic functions similar to 'fun' with nested classes and templates.
>
> This is actually a quite often occuring
> problem and should be part of the FAQ
> (with a short glance on it I couldn't
> find it there).
>

I looked, too, and didn't find it.

> First to the why:
>
> > template<class T> class outer {
> > public:
> > class inner {
> > T m;
> > public:
> > void set(T j) { m = j; }
> > void print(void) {cout << "m=" << m << endl;}
> > T get() { return m; }
>
> I suggest usage of const-correct member functions.
> In this case both print and get are reasonable
> candidates for const functions. This is *not* the
> source of your problem, but you should reconsider
> your design in this regard.
>

Ok, thank you. I was sloppy in constructing the example.

> > // this doesn't work
> > template<class T> T fun ( typename outer<T>::inner &c )
> > { return c.get(); }
>
> The declaration of this function template is
> language-conforming, but can never deduce T
> out of a given outer<T>::inner argument (you
> can only invoke it by providing explicit
> template arguments, which is obviously not
> your intend). The reason for this seemingly
> surprising situation is the nature of templates
> themselves: Since it is possible to specialize
> them either partially or completely, each such
> specialization is independent from the above
> shown primary template.

Ohh.... I missed the fact that you can partially specialize a
template class or redefine inner as not being parameterized by T. It
would be nice to enforce that inner is parameterized by T, however.

> Now you should recognize that the compiler
> can generally *not* backtrack from some
> outer<T>::inner to T.

Yes.


> Second to the solution: Probably the easiest
> one is to add a *non-template* free friend
> function to the outer template like this:
>
> template<class T> class outer {
> public:
> class inner {
> ... // unchanged
> };
>
> friend T fun(inner &c )
> { return c.get(); }
>
> };
>
> This idiom is also known as "Barton-Nackman"
> trick and has it's roots in a very old
> rule of C++ from times where function templates
> could not be overloaded. You should be aware
> that fun here is *not* a member function of
> outer, but just a free function, which will
> only be taken into account via argument-depending
> lookup (ADL).
>

Ok, I think finally get the Barton-Nackman trick. I read this Barton
and Nackman paper a while back ("Algebra for C++ Operators" reprinted
from _C++ Report_ in the book _C++ Gems_). I was inspired by the
category theory part, but the importance of the injected function
declarations escaped me. After your post, I reread the part about
injected function declarations.

Here is what I think is happening. The friend function is injected
into global name space when an outer template class is instantiated.
So, in essence, when I declare

outer<int>::inner I;

given your prescription above with the friend T fun(), this

int fun ( outer<int>::inner &c ) { return c.get(); }

gets put into global name space, and finally, the name is resolved by
argument dependent lookup. Is this correct?

You've cleared up some profound misconceptions and set me
straight. :)

Thanks very much,
Gary

Daniel Krügler

unread,
Jan 30, 2008, 7:17:07 AM1/30/08
to
On 30 Jan., 02:19, Gary Kedziora <gkedzi...@gmail.com> wrote:
> On Jan 27, 6:21 am, "Daniel Krügler" <daniel.krueg...@googlemail.com>
> wrote:
> Ohh.... I missed the fact that you can partially specialize a
> template class or redefine inner as not being parameterized by T. It
> would be nice to enforce that inner is parameterized by T, however.

<nod> There would also exist another solution out
of your problem, depending on the existing relation
between inner and outer. If you would move inner
as an independent template out of outer, it could
have been found, because the deduction situation

template<class T>
T fun(inner<T>&);

belongs to the supported "patterns" and deducable
contexts described in [temp.deduct.type].
Of course this refactoring might destroy a relevant
interaction between inner and outer.

> Ok, I think finally get the Barton-Nackman trick. I read this Barton
> and Nackman paper a while back ("Algebra for C++ Operators" reprinted
> from _C++ Report_ in the book _C++ Gems_). I was inspired by the
> category theory part, but the importance of the injected function
> declarations escaped me. After your post, I reread the part about
> injected function declarations.
>
> Here is what I think is happening. The friend function is injected
> into global name space when an outer template class is instantiated.
> So, in essence, when I declare
>
> outer<int>::inner I;
>
> given your prescription above with the friend T fun(), this
>
> int fun ( outer<int>::inner &c ) { return c.get(); }
>
> gets put into global name space, and finally, the name is resolved by
> argument dependent lookup. Is this correct?

Practically yes. Essentially fun will already be injected,
if outer<int> is instantiated. In earlier (pre-standard?) C++
these injected names were unrestrictedly accessible -
you could even declare 'void fun()' as such, which caused
a lot of surprises - but according to the newer rules only
associated classes can be found via ADL, see [temp.inject]/1+2:

"[..] When a template is instantiated, the names of its friends
are treated as if the specialization had been explicitly
declared at its point of instantiation.[..]
the names of namespace-scope friend functions of a class
template specialization are not visible during an ordinary
lookup unless explicitly declared at namespace scope (11.4).
Such names may be found under the rules for associated
classes (3.4.2)."

> You've cleared up some profound misconceptions and set me
> straight. :)

Your are welcome!

Greetings from Bremen,

Daniel

0 new messages