Как правильно спроектировать функтор, который будет извлекать различные поля из структуры

У меня есть std::vector из structs, которые содержат несколько полей, например:

struct stats
{
   double mean;
   double median;
   double rms;
};
std::vector<stats> data;

Я хотел бы разработать функцию, которая работает на vectorНапример, строит гистограмму. Я хотел бы иметь возможность указать, что подано из structЕсли эта функция работает. Например:

build_histogram(data, get_mean);
build_histogram(data, get_median);
build_histogram(data, get_rms);

Я пытался реализовать некоторые геттеры в stats класс, как это:

struct stats
{
   double mean;
   double median;
   double rms;
   struct get_mean { double operator() () { return mean; };
   struct get_median { double operator() () { return median; };
   struct get_rms { double operator() () { return rms; };
};

Но это говорит о том, что это недопустимое использование нестатических членов mean, median а также rms, Как я могу правильно это реализовать?

2 ответа

Решение

Вы пытаетесь передать тип, а не объект. При выполнении итерации контейнера функтор должен принимать элемент вектора в качестве параметра. Есть несколько способов сделать это, наиболее похожий на то, что вы пытаетесь сделать:

struct stats
{
   double mean;
   double median;
   double rms;
   struct get_mean { double operator() (const stats& s) { return s.mean; } };
   struct get_median { double operator() (const stats& s) { return s.median; } };
   struct get_rms { double operator() (const stats& s) { return s.rms; } };
};

build_histogram(data, stats::get_mean());
build_histogram(data, stats::get_median());
build_histogram(data, stats::get_rms());

Однако, если у вас есть поддержка лямбд (новая в C++11), их проще использовать.

struct stats
{
   double mean;
   double median;
   double rms;
};

build_histogram(data, [](const stats& s) { return s.mean; });
// etc.

Чтобы любой из них работал build_histogram должен быть шаблоном функции со вторым параметром, являющимся любым вызываемым объектом (изменение ReturnType при необходимости).

template <typename F>
ReturnType build_histogram(const std::vector<stats>& data, F functor)
{
     // here you can use functor() or pass it to STL algorithm (e.g. find_if)
}

Различные способы определения функторов (более или менее в порядке моих предпочтений)

  • лямбда-функция (C++11)
  • свободная функция
  • объект functor (это то, что вы сделали, но он не должен быть вложенным в тип)
  • статическая функция-член
  • функции-члены, использующие std::bind (Я бы не советовал это)

Зачем изобретать кортеж?

enum {mean, median, rms};
typedef std::tuple<double,double,double>  stats;
stats   st{1,2,3};
cout << get<mean>(st);
Другие вопросы по тегам