how to avoid code duplication with const and non-const member functions being input into templates

Viewed 78

when attempting to get functions' signatures when they are input into templates its fairly easy, just do the following:

template <class OutType, class... ArgTypes>
void foo(OutType (*func)(ArgTypes...));

it is only marginally more complicated to get a non-static member function:

template <class OutType, class MemberOf, class... ArgTypes>
void foo(OutType (MemberOf::*func)(ArgTypes...));

// or

template <class OutType, class MemberOf, class... ArgTypes>
void foo(OutType (MemberOf::*func)(ArgTypes...) const);

but how exactly do you combine the two function declarations above into one when it doesn't matter whether or not the input method is const?

1 Answers

Unfortunately, the presence or absence of const on a non-static member function is not a feature that can be deduced separately from the function type it appertains to. Therefore, if you want to write a single foo template declaration that is limited to accepting pointers to members (but accepts both const and non-const member functions) then it would have to be:

template <class MemberOf, class F>
void foo(F MemberOf::*func);

For example:

#include <type_traits>

template <class MemberOf, class F>
void foo(F MemberOf::*func) {
    static_assert(std::is_same<F, void(int) const>::value);
}

struct S {
    void bar(int) const {}
};

int main() {
    foo(&S::bar);
}

You cannot have F's argument types deduced at that point. You would have to dispatch to a helper function. (But we cannot deduce all the types at once while also writing a single declaration that accepts both const and non-const. If that's the only thing you'll accept, then sorry, it's not possible.) We can do this like so:

template <class T>
struct remove_mf_const;

template <class R, class... Args>
struct remove_mf_const<R(Args...)> {
    using type = R(Args...);
};

template <class R, class... Args>
struct remove_mf_const<R(Args...) const> {
    using type = R(Args...);
};

template <bool is_const, class F, class OutType, class MemberOf, class... ArgTypes>
void foo_helper(F func, OutType (MemberOf::*)(ArgTypes...)) {
    // now you have all the types you need
}

template <class MemberOf, class F>
void foo(F MemberOf::*func) {
    static_assert(std::is_function<F>::value, "func must be a pointer to member function");
    using NonConstF = typename remove_mf_const<F>::type;
    constexpr bool is_const = !std::is_same<F, NonConstF>::value;
    foo_helper<is_const>(func, (NonConstF MemberOf::*)nullptr);
}

Coliru link

Related