5

I was trying to select a member fn based on some constexpr value. I then tried to call the selected function, but I was getting errors about how I am calling member fn with incorrect syntax.

error: must use '.*' or '->*' to call pointer-to-member function in
'S::SelectedGetter<&S::fn1, &S::fn2>::fn (...)', e.g. '(... ->*
S::SelectedGetter<&S::fn1, &S::fn2>::fn) (...)'     
    18 |     return SelectedGetter<&S::fn1, &S::fn2>::fn();

I tried to call it "properly" but failed. In the end I am using std::invoke, but I wonder if this can be done without std::invoke, using just "raw" C++ syntax.

#include <algorithm>
#include <type_traits>

static constexpr int number = 18;

struct S
{
    using GetterFn = uint32_t(S::*)() const;
    uint32_t fn1()const {
        return 47;
    }
    uint32_t fn2() const {
        return 8472;
    }

    template <GetterFn Getter1, GetterFn Getter2>
    struct SelectedGetter
    {
        static constexpr GetterFn fn = (number < 11) ? Getter1 : Getter2;
    };

    uint32_t f() {
        return std::invoke((SelectedGetter<&S::fn1, &S::fn2>::fn), this);
    }
};

int main() 
{
    return S{}.f() % 100;
}

godbolt link

Note: I am fine with C++20 solutions, for example if some concepts magic can help...

JeJo
  • 30,635
  • 6
  • 49
  • 88
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 1
    The whole point of `std::invoke` is to simplify usage of function pointers, so use that in place of the weird (imo) `->*` syntax. It is also `constexpr` from c++20 if you need that. – Jakub Dąbek Aug 21 '20 at 22:36
  • @JakubDąbek but I presume there is a lot of people who find it harder to read than code in the A – NoSenseEtAl Aug 22 '20 at 08:37

1 Answers1

8

You can call it like normal member function pointer call. The correct syntax would be

 return ((*this).*SelectedGetter<&S::fn1, &S::fn2>::fn)();

or

return (this->*SelectedGetter<&S::fn1, &S::fn2>::fn)();

(See a demo)


Side notes:

  • If the functions you call in f are const, also you could make it also uint32_t f() const
  • Secondly, you can replace the SelectedGetter with a variable template (since ), and now you need less-typing

It will look like

// variable template
template<GetterFn Getter1, GetterFn Getter2>
static constexpr auto fn = (number < 11) ? Getter1 : Getter2;

uint32_t f() const {
   return (this->*fn<&S::fn1, &S::fn2>)();
}

(See a demo)

JeJo
  • 30,635
  • 6
  • 49
  • 88