address of overloaded function/method

334 views
Skip to first unread message

Matthew Woehlke

unread,
Nov 17, 2014, 2:02:31 PM11/17/14
to std-dis...@isocpp.org
Taking the address of an overloaded function or method is painful. From
a language processing standpoint, is there a reason why this needs to be
the case?

IOW, is there some reason it would be totally crazy to allow writing e.g.:

class Foo
{
public:
void bar(int);
void bar(double);
// ...etc.
};

auto p = &Foo::bar(int);

--
Matthew


David Krauss

unread,
Nov 17, 2014, 2:06:59 PM11/17/14
to std-dis...@isocpp.org

On 2014–11–18, at 3:02 AM, Matthew Woehlke <mw_t...@users.sourceforge.net> wrote:

> Taking the address of an overloaded function or method is painful. From
> a language processing standpoint, is there a reason why this needs to be
> the case?

I agree, it’s a problem that should be solved. With lambdas we don’t need to take addresses quite as often, but the wart is still there.

> auto p = &Foo::bar(int);

With this syntax, how would you query an empty argument type list?

Thiago Macieira

unread,
Nov 17, 2014, 2:12:04 PM11/17/14
to std-dis...@isocpp.org
On Tuesday 18 November 2014 03:06:50 David Krauss wrote:
> > auto p = &Foo::bar(int);
>
> With this syntax, how would you query an empty argument type list?

auto p = &Foo::bar(void);

*ugh* /me runs

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

Matthew Woehlke

unread,
Nov 17, 2014, 3:18:29 PM11/17/14
to std-dis...@isocpp.org
On 2014-11-17 14:11, Thiago Macieira wrote:
> On Tuesday 18 November 2014 03:06:50 David Krauss wrote:
>> On 2014–11–18, at 3:02 AM, Matthew Woehlke wrote:
>>> Taking the address of an overloaded function or method is painful. From
>>> a language processing standpoint, is there a reason why this needs to be
>>> the case?
>>
>> I agree, it’s a problem that should be solved. With lambdas we
>> don’t need to take addresses quite as often, but the wart is still
>> there.

It's *required* for the address style of Qt signal/slot connections, at
least for the signal. (A lambda is also still quite a bit uglier than an
address for the slot, and worse, you lose automatic disconnection when
the receiver is destroyed.) So, while lambdas help some cases, not so
much this case :-).

Incidentally, this (Qt signals/slots using addresses) was the specific
pain point that motivated the original question.

>>> auto p = &Foo::bar(int);
>>
>> With this syntax, how would you query an empty argument type list?
>
> auto p = &Foo::bar(void);
>
> *ugh* /me runs

Uhm... right... yeah, you'd need the type there to disambiguate. Well,
that's not the end of the world.

I was also thinking about using something besides ()'s for the argument
list to disambiguate taking an address vs. calling, but not sure that
would be a good idea.

For now I'm using a library solution:

template <typename... Args>
class resolved
{
public:
template <typename Class, typename Result>
auto operator()(Result (Class::*m)(Args...)) const
-> decltype(m)
{ return m; }
};

...which is an immense improvement:

auto p1 = resolved<int>{}(&Foo::bar);
auto p2 = resolved<void>{}(&Foo::bar);

...but I still feel like there should be a simple, clean way...

That said, if anyone can make one of the {}'s or ()'s in the above go
away, I'd be pretty happy with that...

(Before someone (fairly) points out that the existing syntax for
assigning a member pointer isn't all *that* bad, I should note that the
motivating case is where one like to be able to pass a pointer to some
other function/method *without* needing to assign it to a local variable
at all.)

--
Matthew

Myriachan

unread,
Nov 17, 2014, 3:26:32 PM11/17/14
to std-dis...@isocpp.org, mw_t...@users.sourceforge.net
On Monday, November 17, 2014 12:18:29 PM UTC-8, Matthew Woehlke wrote:
On 2014-11-17 14:11, Thiago Macieira wrote:
> On Tuesday 18 November 2014 03:06:50 David Krauss wrote:
>> On 2014–11–18, at 3:02 AM, Matthew Woehlke wrote:
>>> Taking the address of an overloaded function or method is painful. From
>>> a language processing standpoint, is there a reason why this needs to be
>>> the case?

>>>  auto p = &Foo::bar(int);
>>
>> With this syntax, how would you query an empty argument type list?
>
> auto p = &Foo::bar(void);
>
> *ugh* /me runs

Uhm... right... yeah, you'd need the type there to disambiguate. Well,
that's not the end of the world.

I was also thinking about using something besides ()'s for the argument
list to disambiguate taking an address vs. calling, but not sure that
would be a good idea.


It would have to be a different syntax, or you'd break existing code.  You'd need something like the Most Vexing Parse rule to handle ambiguous code like this:

#include <cstdio>

struct Foo
{
   
Foo(int) { }
   
Foo operator()() const { return *this; }
   
Foo Bar(Foo st) { return st; }
   
Foo *operator &() { return nullptr; }
   
void Test(Foo *)
   
{
        std
::printf("'a' is Foo *\n");
   
}
   
void Test(Foo (Foo::*)(Foo (*)()))
   
{
        std
::printf("'a' is Foo (Foo::*)(Foo (*)())\n");
   
}
   
void Meow();
};

void Foo::Meow()
{
   
int x = 4;
   
int *p = &x;
    // Constructing a Foo with *p, or function pointer parameter type?
    auto a = &Foo::Bar(Foo(*p)());
   
Test(a);
}

int main()
{
   
Foo foo(2);
    foo
.Meow();
   
return 0;
}

Melissa

Matthew Woehlke

unread,
Nov 17, 2014, 3:46:46 PM11/17/14
to std-dis...@isocpp.org
On 2014-11-17 15:26, Myriachan wrote:
> It would have to be a different syntax, or you'd break existing code.
> You'd need something like the Most Vexing Parse rule to handle ambiguous
> code like this:
>
> // Constructing a Foo with *p, or function pointer parameter type?
> auto a = &Foo::Bar(Foo(*p)());

Um... the former? I don't see how it would be a function pointer; how
does 'Foo(*p)()' name a type?

Overload resolution would only occur in the presence of a parenthetical
list of types. Right now AFAIK such a construct is illegal except where
it represents a parameter list, i.e. as part of the specification of a
function pointer type, or as part of a function declaration. I'd also
consider going one step further and only apply it when '&' is present,
although I don't know that that actually helps in any cases.

--
Matthew

Myriachan

unread,
Nov 17, 2014, 4:08:27 PM11/17/14
to std-dis...@isocpp.org, mw_t...@users.sourceforge.net

When referring to a function type or function pointer type, it's legal but ignored to provide names for the parameters.  This is an inherited rule from C.

#include <cstdio>

template <void Function(int x)>
void Meow()
{
   
Function(2);
};

void Kitty(int y)  // different name OK
{
    std
::printf("Kitty: %d\n", y);
}

int main()
{
   
Meow<Kitty>();
   
return 0;
}

Melissa

Thiago Macieira

unread,
Nov 17, 2014, 4:11:23 PM11/17/14
to std-dis...@isocpp.org
On Monday 17 November 2014 15:18:12 Matthew Woehlke wrote:
> auto p1 = resolved<int>{}(&Foo::bar);
> auto p2 = resolved<void>{}(&Foo::bar);
>
> ...but I still feel like there should be a simple, clean way...
>
> That said, if anyone can make one of the {}'s or ()'s in the above go
> away, I'd be pretty happy with that...
>
> (Before someone (fairly) points out that the existing syntax for
> assigning a member pointer isn't all *that* bad, I should note that the
> motivating case is where one like to be able to pass a pointer to some
> other function/method *without* needing to assign it to a local variable
> at all.)

There is a way.

connect(sender, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
[](int exitCode) { ... });

Matthew Woehlke

unread,
Nov 17, 2014, 5:43:06 PM11/17/14
to std-dis...@isocpp.org
On 2014-11-17 16:08, Myriachan wrote:
> On Monday, November 17, 2014 12:46:46 PM UTC-8, Matthew Woehlke wrote:
>>
>> On 2014-11-17 15:26, Myriachan wrote:
>>> It would have to be a different syntax, or you'd break existing code.
>>> You'd need something like the Most Vexing Parse rule to handle ambiguous
>>> code like this:
>>>
>>> // Constructing a Foo with *p, or function pointer parameter type?
>>> auto a = &Foo::Bar(Foo(*p)());
>>
>> Um... the former? I don't see how it would be a function pointer; how
>> does 'Foo(*p)()' name a type?
>>
>> Overload resolution would only occur in the presence of a parenthetical
>> list of types. Right now AFAIK such a construct is illegal except where
>> it represents a parameter list, i.e. as part of the specification of a
>> function pointer type, or as part of a function declaration. I'd also
>> consider going one step further and only apply it when '&' is present,
>> although I don't know that that actually helps in any cases.
>
> When referring to a function type or function pointer type, it's legal but
> ignored to provide names for the parameters. This is an inherited rule
> from C.

Yes, but we don't need to propagate that rule to this, as it's a new
feature. We could disallow parameter names here.

Anyway, I fail to see what this has to do with the question?

--
Matthew

Matthew Woehlke

unread,
Nov 17, 2014, 5:50:06 PM11/17/14
to std-dis...@isocpp.org
On 2014-11-17 16:11, Thiago Macieira wrote:
> On Monday 17 November 2014 15:18:12 Matthew Woehlke wrote:
>> auto p1 = resolved<int>{}(&Foo::bar);
>> auto p2 = resolved<void>{}(&Foo::bar);
>>
>> ...but I still feel like there should be a simple, clean way...
>>
>> (Before someone (fairly) points out that the existing syntax for
>> assigning a member pointer isn't all *that* bad, I should note that the
>> motivating case is where one like to be able to pass a pointer to some
>> other function/method *without* needing to assign it to a local variable
>> at all.)
>
> There is a way.
>
> connect(sender, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
> [](int exitCode) { ... });

Ugh... that's even worse than my 'resolved' helper:

connect(sender, resolved<int>{}(&QProcess::finished), ...);

At least I didn't have to repeat the class name (or mention the return
type at all) :-).

--
Matthew

Miro Knejp

unread,
Nov 17, 2014, 6:12:11 PM11/17/14
to std-dis...@isocpp.org, mw_t...@users.sourceforge.net
On Monday, November 17, 2014 9:18:29 PM UTC+1, Matthew Woehlke wrote:
That said, if anyone can make one of the {}'s or ()'s in the above go
away, I'd be pretty happy with that...


template<class... Args, class T, class R>
auto resolved(R (T::*m)(Args...)) { return m; }

template<class T, class R>
auto resolved(R (T::*m)()) { return m; }

auto p1 = resolved<int>(&Foo::bar); 
auto p2 = resolved<>(&Foo::bar);

There you go.

Matthew Woehlke

unread,
Nov 17, 2014, 6:19:58 PM11/17/14
to std-dis...@isocpp.org
On 2014-11-17 18:12, Miro Knejp wrote:
> On Monday, November 17, 2014 9:18:29 PM UTC+1, Matthew Woehlke wrote:
>> That said, if anyone can make one of the {}'s or ()'s in the above go
>> away, I'd be pretty happy with that...
>
> template<class... Args, class T, class R>
> auto resolved(R (T::*m)(Args...)) { return m; }

Er... huh. I thought you weren't allowed to give arguments after the
'...'. Thanks.

Anyone interested in adding that to 'std'? :-)

--
Matthew

Miro Knejp

unread,
Nov 17, 2014, 6:40:28 PM11/17/14
to std-dis...@isocpp.org, mw_t...@users.sourceforge.net
Well, as a basic rule if the arguments after the pack are deduced it's fine. The actual rules are a bit more complicated though (as everything related to templates...).

This makes me wonder, why not std::mem_fn?

auto p1 = std::mem_fn<int(int)>(&Foo::bar);

Sure, you have to specify the return type and don't get a pointer-to-member but it's definitely an improvement. Plus, it works for data members. If the target interface accepts any callable this should do fine. One could probably extend mem_fn to work without specifying the return type like your resolved() does.

Thiago Macieira

unread,
Nov 18, 2014, 12:22:17 AM11/18/14
to std-dis...@isocpp.org
On Monday 17 November 2014 15:40:28 Miro Knejp wrote:
> Sure, you have to specify the return type and don't get a pointer-to-member
> but it's definitely an improvement. Plus, it works for data members. If the
> target interface accepts any callable this should do fine. One could
> probably extend mem_fn to work without specifying the return type like your
> resolved() does.

For our case in Qt, we need the actual PMF as well as the actual class the
function is defined in, so we can compare against a pre-determined list of PMFs
that are known to be signals.
Reply all
Reply to author
Forward
0 new messages