4

I have to provide an overload set of f, that accepts both member and member function pointers:

void g(int) {}

template <typename T, typename Field>
void f(const T& t, Field T::*field) { g(t.*field); }

template <typename T, typename Field>
void f(const T& t, Field (T::*getter)() const) { g((t.*getter)()); }

struct Foo {
  int x = 0;
  int y() const noexcept { return 1; }
};

int main() {
  const Foo foo;
  f(foo, &Foo::x);
  f(foo, &Foo::y);
}

This works fine in C++11 and C++14, but breaks in C++17, because as of P0012R1, the noexcept specifier is part of the function type. To resolve this, an additional overload has to be added:

#if __cplusplus >= 201703L

template <typename T, typename Field>
void f(const T& t, Field (T::*getter)() const noexcept) { g((t.*getter)()); }

#endif

The macro guard are necessary, otherwise the code does not compile with older standards, such as C++11 or C++14 (the error is about redefinition of a function template).

As shown above, the implementation of both overloads are the same. Is it possible to provide a single overload that works in C++14 and C++17, without conditional compilation (#if/endif)? The goal is reduction of complexity, code duplication and testing burden.

Actual use case: https://github.com/Morgan-Stanley/binlog/pull/59/files#diff-043a057ac0b43822d0084562ace76697

erenon
  • 18,838
  • 2
  • 61
  • 93

1 Answers1

5

Yes. Just write the one overload, total, and use std::invoke:

template <typename T, typename F>
void f(const T& t, F f) { g(std::invoke(f, t)); }

While std::invoke itself is C++17, it is implementable in C++11 - and it's probably just worth doing since it's generally useful. This approach not only handles noexcept member functions in C++17, but also reference-qualified and non-const-qualified member functions in C++11.

Although C++11 itself also contains an implementation of std::invoke - just in an unexpected place: std::reference_wrapper<T>:

template <typename T, typename F>
void f(const T& t, F f) { g(std::ref(f)(t)); }
Barry
  • 286,269
  • 29
  • 621
  • 977