Перегрузка независимо от спецификации - PullRequest
4 голосов
/ 25 марта 2020

Я должен предоставить набор перегрузки f, который принимает указатели на функции-члены и функции-члены:

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);
}

Это прекрасно работает в C ++ 11 и C ++ 14, но не работает C ++ 17, поскольку с P0012R1 спецификатор noexcept является частью типа функции. Для решения этой проблемы необходимо добавить дополнительную перегрузку:

#if __cplusplus >= 201703L

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

#endif

Необходим макрос защиты, иначе код не будет компилироваться со старыми стандартами, такими как C ++ 11 или C ++ 14 ( ошибка связана с переопределением шаблона функции).

Как показано выше, реализация обеих перегрузок одинакова. Можно ли обеспечить одну перегрузку, которая работает в C ++ 14 и C ++ 17, без условной компиляции (# if / endif)? Целью является снижение сложности, дублирования кода и бремени тестирования.

Фактический пример использования: https://github.com/Morgan-Stanley/binlog/pull/59/files#diff -043a057ac0b43822d0084562ace76697

1 Ответ

5 голосов
/ 25 марта 2020

Да. Просто запишите одну перегрузку, итого и используйте std::invoke:

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

Хотя std::invoke сам по себе является C ++ 17, он реализуем в C ++ 11 - и это, вероятно, просто стоит сделать, так как это обычно полезно. Этот подход не только обрабатывает noexcept функции-члены в C ++ 17, но также и справочные и неконстантные функции-члены в C ++ 11.

Хотя сам C ++ 11 также содержит реализация std::invoke - просто в неожиданном месте: std::reference_wrapper<T>:

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