7

I saw this code snippet in the example on cppreference for std::is_function and I don't understand how it works. Could someone explain to me why U gets deduced as it does in PM_traits?

struct A {
  int fun() const&;
};

template<typename>
struct PM_traits {}; 

template<class T, class U>
struct PM_traits<U T::*> {
  using member_type = U;
};

int main() {
  using T = PM_traits<decltype(&A::fun)>::member_type; // T is int() const&
}
willtunnels
  • 106
  • 4
  • I've verified that this is the deduced type with g++. `T` is in fact `int() const&`. – willtunnels Jun 21 '19 at 19:47
  • Well, it seems you are right. But I have a hard time understanding what a `int() const&` can possibly mean outside the context of a member function. `const` and the ref qualifier `&` are meaningless (and forbidden) on a free function. – François Andrieux Jun 21 '19 at 19:49
  • @FrançoisAndrieux you can simply it, by making a simple member function, non-const. It would still be a function type. – SergeyA Jun 21 '19 at 19:51
  • 1
    @FrançoisAndrieux Yes, they are forbidden on free functions, but not on function types in general. Remember that it's legal to declare (not define) a member function using a typedef, for example. [Live example](http://coliru.stacked-crooked.com/a/6e27c347a58783f3) – Angew is no longer proud of SO Jun 21 '19 at 19:51
  • @Angew That's fascinating, thank you for the example. – François Andrieux Jun 21 '19 at 19:56
  • Somewhat related, on qualified function types: https://stackoverflow.com/questions/17446220/c-function-types/17446559 – aschepler Jun 21 '19 at 20:33

1 Answers1

11

U T::* is a type such that when we have U T::* p, p points to a member of class T, and that member is of type U.

fun is a function of type int () const &: a const &-qualified function taking no parameters and returning int, and it's a member of class A. Therefore, in the deduction, T is deduced to A and U is deduced to the type of A::fun, which is int () const &.


It may look a bit confusing, because if the type of &A::fun was spelled out explicitly, it would have to be written int (A::*)() const &. However, in the template's case, the type int () const & is "hidden" behind the name U, so the pointer to member is then just U A::*. It's similar to how type names can be used to simplify the syntax of normal function pointers:

int foo(char, double) { return 42; }

using Fun = int (char, double);
Fun *p = &foo;
// instead of:
int (*q)(char, double) = &foo;

The same happens in the template, just with A::* instead of *.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • I can write `int(A::* p)() const & = &A::fun;` but `int () const & A::* p = &A::fun;` gives me an error. Why is there a discrepancy between what happens here and what happens in templates? – willtunnels Jun 21 '19 at 20:01
  • @willtunnels Because that's how C++ syntax works. Notice that you *can* write `using V = int () const &; V A::* p = &A::fun;`. It's the same as writing `const int *p;` vs. `using T = int*; const T p;`. – Angew is no longer proud of SO Jun 21 '19 at 20:17
  • That generally seems to work except when ref qualifiers are introduced. I put in your code exactly and g++ spat out `cannot convert 'int (A::*)() const &' to 'int (A::*)() const' in initialization`. Am I missing something obvious? Can someone confirm that that code actually works for them? (Thanks for your help by the way.) – willtunnels Jun 21 '19 at 21:57
  • So, based on some experimentation, it looks like clang handles this correctly (?) but gcc fails. – willtunnels Jun 21 '19 at 22:16