2

While answering a question, I proposed utilizing template aliases for typedefing the signature of a member function; that is, not just typedefing a member function but being able to factor out the target class that contains the method:

template<typename T>
using memberf_pointer = int (T::*)(int, int); 

Though this seems to cover what the question asked, I tried to generalize it for arbitrary function arguments:

template<typename T, typename... Args>
using memberf_pointer = int (T::*)(Args&&...); 

It fails with argument deduction issues (basically it assumes an empty arument list). Here's a demo:

#include <iostream>

class foo
{
public:
  int g (int x, int y) { return x + y ; }
};

template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args&&...); 

int main()
{
  foo f ;
  memberf_pointer<foo> mp = &foo::g ;
  std::cout << (f.*mp) (5, 8) << std::endl ;
}

Why is this? Is there a way to get it to work?

Community
  • 1
  • 1
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • Why not use `auto` simply? `auto mp = &foo::g ;` should work. – Nawaz Jul 27 '15 at 10:23
  • Assigning an rvalue is not a deduction context for the member function arguments. There are workarounds though: [Solution 1](http://coliru.stacked-crooked.com/a/a084cda5dcd66cd4) and [Solution 2](http://coliru.stacked-crooked.com/a/e120006251673691) – Marco A. Jul 27 '15 at 10:29
  • @Nawaz I'm playing with the command pattern and want to assure I'm passing a member function of a receiver to the construction of the command object. In that context a 'conscious' typedef would be nice to have (the other option would be to build a trait system or use concepts). `auto` is wonderful alas not in my case – Nikos Athanasiou Jul 27 '15 at 11:25

4 Answers4

1

Why not simply use an auto in this case? The only advantage of having a template in this case is being able to provide your types explicitly.

On the other hand, if you want your template to automatically deduce a function type, it needs to be directly parametrized on that type. If you also want it to provide all of the building blocks for you, the simplest way is to specialize it for functions or member functions. Example:

template<typename T> struct memberf_pointer_descriptor;

template<typename TOwner, typename TRet, typename... Args>
struct memberf_pointer_descriptor<TRet(TOwner::*)(Args...)>
{
    // Your stuff goes here.
    using type = TRet(TOwner::*)(Args...);
};

memberf_pointer_descriptor<decltype(&foo::g)>;

Or a function template that directly takes foo::g as an argument, to mitigate the need of using an explicit decltype. Depends on your needs.

ing
  • 81
  • 3
  • Here you just implement `identity` type for member functions. – Jarod42 Jul 27 '15 at 11:28
  • I'm playing with the command pattern and want to assure I'm passing a member function of a receiver to the construction of the command object. In that context a 'conscious' typedef would be nice to have (the other option would be to build a trait system or use concepts). `auto` is wonderful alas not in my case – Nikos Athanasiou Jul 27 '15 at 11:29
1

A way to make your example work is the following:

#include <iostream>

class foo
{
public:
    int g(int x, int y) { return x + y; }
};

template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args...);

int main()
{
    foo f;
    memberf_pointer<foo, int, int> mp = &foo::g;
    std::cout << (f.*mp) (5, 8) << std::endl;
}

It removes the reference on the variadic template parameter and when instantiating the memberf_pointer it supplies also the member function parameters. But auto is probably the way to go...

marom
  • 5,064
  • 10
  • 14
  • I'm playing with the command pattern and want to assure I'm passing a member function of a receiver to the construction of the command object. In that context a 'conscious' typedef would be nice to have (the other option would be to build a trait system or use concepts). `auto` is wonderful alas not in my case – Nikos Athanasiou Jul 27 '15 at 11:29
1

C++ doesn't feature something like a Hindley-Milner type deduction system and it won't work for your specific rvalue assignment

memberf_pointer<foo> mp = &foo::g ;

As for some quick workarounds you could

  1. Just drop the whole struct and use auto

    auto mp = &foo::g;
    
  2. Explicitly provide the types or the pointer type

    template<typename T>
    using memberf_pointer = T;
    
    memberf_pointer<decltype(&foo::g)> mp = &foo::g;
    

Cfr. Template argument deduction

Marco A.
  • 43,032
  • 26
  • 132
  • 246
1

The wording in both the title and body of the question is very misleading. There is zero template deduction going on in your example, anywhere. When you write:

memberf_pointer<foo> mp = &foo::g;

memberf_pointer<foo> is an alias template, yes, but it's a specific instantiation of one. There is no deduction going on because you are providing the exact type of mp. That line is exactly equivalent to:

int (foo:*mp)() = &foo::g;

which doesn't compile for the obvious reason that g takes arguments. The way to get template deduction in an assignment statement is to use auto:

auto mp = &foo::g;

The type of mp will be the same type as U had you called:

template <typename U> void meow(U );
meow(&foo::g);

which is to say, int (foo::*)(int, int).

Similarly, you could do:

decltype(&foo::g) mp = &foo::g;

Which would give you the same type as before.

Of course, even if you provided the correct argument list:

memberf_pointer<foo, int, int> mp = &foo::g;

That still wouldn't compile since your alias adds rvalue references to both arguments. The type of mp there is int (foo::*)(int&&, int&&), which would not match &foo::g. Perhaps you'd intended this to be deduction as if by forwarding-reference, but that is not the case here. In order to use the alias correctly you'd have to rewrite it:

template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args...); 

memberf_pointer<foo, int, int> mp = &foo::g;

Had we had a member function that took an rvalue reference, we could then explicitly provide it:

class bar
{
public:
  int h(int&& x, int&& y) { return x + y ; }
};

memberf_pointer<bar, int&&, int&&> mb = &bar::h;
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Could I have a comment on `memberf_pointer mp = &foo::g` being an rvalue assignment (see marco's answer) ? – Nikos Athanasiou Jul 27 '15 at 14:19
  • @NikosAthanasiou `&foo::g` is an rvalue, but that's not relevant here (if you had `auto fg = &foo::g; memberf_pointer mp = fg`, it'd be an "lvalue assignment" and still fail for the exact same reason). The main point is that there's no type deduction when you explicitly specify types. – Barry Jul 27 '15 at 14:21