Overload lookup of pointer-to-member

J

jknupp

In the following program, if the call to bar does not specify the type
as <int>, gcc gives the error "no matching function for call to
‘bar(A&, <unresolved overloaded function type>)’". Since bar
explicitly takes a pointer-to-member with no parameters, why is the
lookup for the overloaded function not able to use the number of
arguments to determine the appropriate function? Is there a relevant
portion of the standard that governs the lookup rules in this case?

Thanks in advance,
Jeff

#include <iostream>
using namespace std;

class A
{
public:
int foo()
{
cout << "int foo" << endl;
return 5;
}
template <class A>
void foo (A a)
{
cout << "template foo" << endl;
}
};

template <class Type, class Obj>
void bar (Obj& obj, Type (Obj::*func)())
{
Type t = (obj.*func)();
cout << "TYPE: " << t << endl;
}
int main ()
{
A a;
bar<int>(a, &A::foo); //<int> required, even though only one of the
functions takes no arguments
bar(a, &A::foo); //doesn't compile
}
 
A

Abhishek Padmanabh

In the following program, if the call to bar does not specify the type
as <int>, gcc gives the error "no matching function for call to
‘bar(A&, <unresolved overloaded function type>)’". Since bar
explicitly takes a pointer-to-member with no parameters, why is the
lookup for the overloaded function not able to use the number of
arguments to determine the appropriate function? Is there a relevant
portion of the standard that governs the lookup rules in this case?

Thanks in advance,
Jeff

#include <iostream>
using namespace std;

class A
{
public:
int foo()
{
cout << "int foo" << endl;
return 5;
}
template <class A>
void foo (A a)
{
cout << "template foo" << endl;
}

};

template <class Type, class Obj>
void bar (Obj& obj, Type (Obj::*func)())
{
Type t = (obj.*func)();
cout << "TYPE: " << t << endl;}

int main ()
{
A a;
bar<int>(a, &A::foo); //<int> required, even though only one of the
functions takes no arguments
bar(a, &A::foo); //doesn't compile
}

Compiles fine with Comeau online and VC++ 2005. So, could be there's
something wrong with g++ (an older version, may be).
 
J

jknupp

Compiles fine with Comeau online and VC++ 2005. So, could be there's
something wrong with g++ (an older version, may be).

I had tried using gcc 4.2.3 (where it failed) and VC++6 (where it
worked). I'm really interested in if it's supposed to work (i.e. both
comeau and VC++ are complying with the standard in this case) or an
actual bug in gcc.

Thanks,
Jeff
 
C

courpron

I had tried using gcc 4.2.3 (where it failed) and VC++6 (where it
worked). I'm really interested in if it's supposed to work (i.e. both
comeau and VC++ are complying with the standard in this case) or an
actual bug in gcc.

Thanks,
Jeff

GCC is right because the overload resolution set of &A::foo contains a
function template.

Alexandre Courpron.
 
J

James Kanze

Compiles fine with Comeau online and VC++ 2005. So, could be
there's something wrong with g++ (an older version, may be).

I don't know. The problem isn't overload resolution per se, but
template type deduction, which has a completely different set of
rules. In this case, I'll admit that I don't understand the
rules enough to be sure, but off hand, I don't think it is
supposed to work. The compiler can't effectively do the type
deduction for bar until it has resolved the overloading of
&A::foo, and it can't resolve this overloading until it knows
the type &A::foo is being used to initialize.
 
J

jknupp

I don't know.  The problem isn't overload resolution per se, but
template type deduction, which has a completely different set of
rules.  In this case, I'll admit that I don't understand the
rules enough to be sure, but off hand, I don't think it is
supposed to work.  The compiler can't effectively do the type
deduction for bar until it has resolved the overloading of
&A::foo, and it can't resolve this overloading until it knows
the type &A::foo is being used to initialize.
In this case, though, it shouldn't necessarily need to know the return
type to determine the proper overloaded function as it can use the
number of arguments. I'm just a bit confused about the order in which
the two actions are applied (the type deduction and the overload
resolution) and the rules/precedence for resolving overloads when
function templates are involved.
--
James Kanze (GABI Software)             email:[email protected]
Conseils en informatique orientée objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Thanks,
Jeff
 
J

James Kanze

[...]
In this case, though, it shouldn't necessarily need to know
the return type to determine the proper overloaded function as
it can use the number of arguments.

It can't even start overload resolution until it has done type
deduction. Until the compiler has deduced the type of bar, it
can't consider what instances of foo are value. And until knows
the type of &A::foo, it can't deduce the type of bar.
I'm just a bit confused about the order in which the two
actions are applied (the type deduction and the overload
resolution) and the rules/precedence for resolving overloads
when function templates are involved.

The complete rules are far from simple, but the basic principle
is that the compiler first constructs a set of all possible
overloaded, then does type deduction on the templates, replacing
each template in the initial set with an actual instantiation
(or dropping it completely---substitution failure is not an
error), and only then procedes to overload resolution (the first
step of which is to throw out any functions which cannot be
called. In this particular case, there are two overload
resolutions involved: one for bar and one for foo, but that
doesn't change the basic principles. In order to procede with
the process for &A::foo, it must know the exact target type.
And in order to know the target type, it must have successfully
resolved the overload of bar, which means that it has to have
successfully done type deduction for the template bar. Which
requires knowing which overload of &A::foo was chosen.

As an intelligent human, you can easily solve the problem, but
if you think about it, in solving it, you jump back and forth,
first elimiating some of the overloads for foo (without having
all of the necessary information for full overload resolution),
using intuitive information about the possible results. The
compiler can only do one thing after the other, and in this
case, you have a circular dependency for it: to do A, it must
first do B, and to do B, it must first do A. It doesn't have
enough imagination to be able to speculatively do A and B in
parallel.
 
C

courpron

In this case, though, it shouldn't necessarily need to know the return
type to determine the proper overloaded function as it can use the
number of arguments. I'm just a bit confused about the order in which
the two actions are applied (the type deduction and the overload
resolution)

Overload resolution comes before type deduction.
and the rules/precedence for resolving overloads when
function templates are involved.

As I said earlier, when function templates are involved, type
deduction fails. Your code is non-standard. It would be standard if
you replaced the function template foo by a non-template version.

Alexandre Courpron.
 
J

jknupp

Overload resolution comes before type deduction.
Understood. Do you know where this is mentioned in the standard?
As I said earlier, when function templates are involved, type
deduction fails. Your code is non-standard. It would be standard if
you replaced the function template foo by a non-template version.

Assuming you are saying that this particular combination of type
deduciton and overload resolution will not work, and not that the
standard prevents you from overloading a funciton template with a non-
template function (which is not true), is there a way to acheive the
same effect in a compliant way? In the acutal code, the bar function
takes additional parameters (a field name) and is used to copy a
subset of the values of A (the foo functions represent accessors and
mutators of A) on a seperate object. What I would rather not do is be
which would be different for each said:
Alexandre Courpron.- Hide quoted text -

- Show quoted text -

Thanks,
Jeff
 
J

jknupp

    [...]




In this case, though, it shouldn't necessarily need to know
the return type to determine the proper overloaded function as
it can use the number of arguments.

It can't even start overload resolution until it has done type
deduction.  Until the compiler has deduced the type of bar, it
can't consider what instances of foo are value.  And until knows
the type of &A::foo, it can't deduce the type of bar.
I'm just a bit confused about the order in which the two
actions are applied (the type deduction and the overload
resolution) and the rules/precedence for resolving overloads
when function templates are involved.

The complete rules are far from simple, but the basic principle
is that the compiler first constructs a set of all possible
overloaded, then does type deduction on the templates, replacing
each template in the initial set with an actual instantiation
(or dropping it completely---substitution failure is not an
error), and only then procedes to overload resolution (the first
step of which is to throw out any functions which cannot be
called.  In this particular case, there are two overload
resolutions involved: one for bar and one for foo, but that
doesn't change the basic principles.  In order to procede with
the process for &A::foo, it must know the exact target type.
And in order to know the target type, it must have successfully
resolved the overload of bar, which means that it has to have
successfully done type deduction for the template bar.  Which
requires knowing which overload of &A::foo was chosen.

Thanks, this is what I had assumed. What is still a bit unclear to me,
though, is that Type is being used as the return type of foo, which I
didn't think participated in overload resolution. At the point that it
is resolving the overload for foo (and thus deducing the types for
bar), wouldn't Type be extraneous information? If not, it seems that
if you were to replace the templated foo with 'int foo(int arg)' that
the amount of information is the same with respect to overload
resolution, yet the latter case will work.
As an intelligent human, you can easily solve the problem, but
if you think about it, in solving it, you jump back and forth,
first elimiating some of the overloads for foo (without having
all of the necessary information for full overload resolution),
using intuitive information about the possible results.  The
compiler can only do one thing after the other, and in this
case, you have a circular dependency for it: to do A, it must
first do B, and to do B, it must first do A.  It doesn't have
enough imagination to be able to speculatively do A and B in
parallel.

Again, I understand you point here, but that presupposes that they
return type for foo is relevant. I was under the impression that it
wasn't for overload resolution (at least at the resolution stage we
are at), but obviously may be mistaken.

Thanks,
Jeff
 
J

James Kanze

On Jul 13, 4:47 am, James Kanze <[email protected]> wrote:

[...]
Thanks, this is what I had assumed. What is still a bit
unclear to me, though, is that Type is being used as the
return type of foo, which I didn't think participated in
overload resolution.

It doesn't, but...

In your example, the overload resolution of &T::foo was a
special case; the overload resolution of &T::foo depends on the
type it is initializing or being assigned to. And until the
compiler has done the type deduction of bar, the parameters
don't have a type, so neither type deduction nor overload
resolution can proceed. The return type of the pointer to
function parameter will be ignored once overload resolution for
&T::foo is invoked, but the compiler can't even get that far.
At the point that it is resolving the overload for foo (and
thus deducing the types for bar), wouldn't Type be extraneous
information?

If the compiler got that far.
If not, it seems that if you were to replace the templated foo
with 'int foo(int arg)' that the amount of information is the
same with respect to overload resolution, yet the latter case
will work.

No, because the compiler can't start working until it has
deduced the actual type of foo. Which in turn depends on the
actual type of the parameter. Which depends on the type of the
argument. Which is what we're trying to deduce. The fact that
the return type will not be used in the overload resolution
later doesn't stop the compiler from having to deduce it. In
the end, the compiler will have to know the return type of both
the pointer to member function, and the function whose address
is being assigned to it. And there are no provisions for the
compiler to do partial type deduction on the parameter, skipping
the return type, then do overload resolution (with the
associated type deduction on the template functions), then come
back and finish the type deduction on bar.
Again, I understand you point here, but that presupposes that
they return type for foo is relevant.

It is, in the end. It is needed to deduce the template
arguments for bar. And until those template arguments are
deduced, the "pointer to function" doesn't have a known type,
and the compiler cannot proceed with overload resolution on the
argument. The fact that the return type is not used in this
overload resolution is irrelevant; for the overload resolution
to start, the type must be known.

(Actually, I'm not even 100% certain that you can say that the
return type is irrelevant here. Taking the address of a
function is a special case, where the use is taken into account.
But it doesn't matter, because you don't get that far.)
I was under the impression that it wasn't for overload
resolution (at least at the resolution stage we are at), but
obviously may be mistaken.

Again, you're failing before overload resolution starts.
 
C

courpron

Understood. Do you know where this is mentioned in the standard?


14.8.2.2/1 : "Template arguments can be deduced from the type
specified when taking the address of an overloaded function."


As soon as type deduction handles adresses of overload functions, then
the whole type deduction process must complete after overload
resolution (otherwise it could lead to ambiguous cases) . However, I
would rather see overload resolution as a step in the type deduction
process and that step is generally the final one.

But, in our case, it is not the final step. If some types of the
signature of the overloaded function must be deduced and that
deduction can't be "directly" done from the function call (explicit
template specialization,...), then there is what we can call "template
overload resolution" : type deduction will check if the overload set
provides a match for the "template signature" (that overload set must
not contain function template). From that "template overload
resolution", type deduction can take from the matched candidate the
correct types.

Assuming you are saying that this particular combination of type
deduciton and overload resolution will not work, and not that the
standard prevents you from overloading a funciton template with a non-
template function (which is not true), is there a way to acheive the
same effect in a compliant way? In the acutal code, the bar function
takes additional parameters (a field name) and is used to copy a
subset of the values of A (the foo functions represent accessors and
mutators of A) on a seperate object. What I would rather not do is be
forced to specify the bar<int>, which would be different for each
accessor of A and subject to change.

If it's different for each accessor and subject to change, then
encapsulate it.
Slight modification of your example :

#include <iostream>
using namespace std;

class A
{
public:
typedef int return_type; // type trait
int foo()
{
cout << "int foo" << endl;
return 5;
}
template <class A>
void foo (A a)
{
cout << "template foo" << endl;
}
};

template <class Obj>
void bar (Obj& obj, typename Obj::return_type (Obj::*func)())
{
typename Obj::return_type t = (obj.*func)();
cout << "TYPE: " << t << endl;
}
int main ()
{
A a;
bar(a, &A::foo); // should compile on a conforming compiler
}


Here, every part of the foo signature can be directly deduced from the
function call (from &A::foo) without resorting to "template overload
resolution", then it doesn't matter whether there is a function
template in the foo overload set.


Alexandre Courpron.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top