Как явно привести аргумент к ожидаемому параметру функции? - PullRequest
0 голосов
/ 10 ноября 2018

Я пытаюсь написать универсальную функцию для вычисления среднего по определенному диапазону.

template <typename Range, typename Ret, typename Func>
Ret average(Range range, Ret zero, Func extract) {
  Ret sum = zero;
  int numElements = 0;
  for (const auto& elem : range) {
    sum += extract(elem);
    ++numElements;
  }
  if (numElements > 0)
    sum /= numElements;
  return sum;
}

Проблема, с которой я сталкиваюсь, связана с использованием оператора /=, но чтобы лучше уточнить аргументы этой функции, позвольте мне уточнить их:

  • Range range - это любой объект, который определяет диапазон между begin() и end() функциями-членами. Мне может понадобиться добавить const&, чтобы избежать ненужного копирования.
  • Ret zero определяет нейтральный элемент сложения, используемый при вычислении среднего. Это может быть просто скаляр, но он будет работать, например, с векторами или матрицами.
  • Func extract - это функция (обычно задается как лямбда-функция), которая преобразует элементы диапазона в значения Ret, которые я усредняю. На практике я использую его как получатель определенного поля в больших объектах, по которым я перебираю.

    Я мог бы, вероятно, определить его как std::function<Ret(decltype(*range.begin()))> или что-то подобное, если бы у C ++ не было проблем с выводом типов таким образом.

Я предполагаю, что Ret предоставляет некоторый /= оператор, с которым может работать вышеуказанная функция, но я не хочу требовать, чтобы она взяла конкретно int.

В моем случае, например, Ret работает с float -s, и это дает мне досадное предупреждение:

warning: 'argument': conversion from 'int' to 'float', possible loss of data

Итак, Какие у меня есть варианты сделать вышеуказанную функцию чистой и работать с любым подходящим operator/=?


Я пытался, например, вывести тип правильного аргумента оператора и явно привести к нему:

template <typename Range, typename Ret, typename Func>
Ret average(Range range, Ret zero, Func extract) {
  Ret sum = zero;
  int numElements = 0;
  for (const auto& elem : range) {
    sum += extract(elem);
    ++numElements;
  }
  using F = std::remove_pointer<decltype(&Ret::operator/=)>;

  if (numElements > 0)
    sum /= static_cast<typename boost::function_traits<F>::arg1_type>(numElements);
  return sum;
}

Но я получаю много ошибок компиляции, предполагая, что я не знаю, что я делаю. Начинается с:

error: 'boost::detail::function_traits_helper<std::remove_pointer<SpecificTypeUsedAsRet &(__cdecl SpecificTypeUsedAsRet::* )(float)> *>': base class undefined

Это возможно потому, что boost::function_traits не работает с функциями-членами, только с обычными?

Меня также беспокоит, что это решение может не работать, когда:

  • operator/= задается не как функция-член, а как обычная функция с двумя аргументами.
  • operator/= перегружен относительно своего правого операнда. int может соответствовать только одной из перегрузок - поэтому нет никакой двусмысленности, но decltype не будет знать, какую перегрузку принять.
  • Я бы предпочел не использовать boost, но придерживаться возможностей, предоставляемых новейшими стандартами C ++

1 Ответ

0 голосов
/ 11 ноября 2018

Вы можете просто объявить Ret numElements = 0; вместо того, чтобы сделать его int. Если он имеет оператор /=, он, вероятно, имеет оператор ++; или вы можете использовать num_elements += 1 вместо.

...