Я пытаюсь написать универсальную функцию для вычисления среднего по определенному диапазону.
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 ++