Мне нужна была возможность писать такой код:
SplineFunction<Polynomial<3>> cubicSplineFunction;
// ... here be some additional code to populate the above object ...
auto dydx = cubicSplineFunction.transform<Polynomial<2>>(const Polynomial<3>& cubicSpline){
return cubicSpline.derivative();
};
auto dsdx = cubicSplineFunction.transform<T/*?*/>([](const Polynomial<3>& cubicSpline){
Polynomial<2> dy = cubicSpline.derivative();
Polynomial<4> dsSquared = dy*dy + 1*1;
return [dsSquared](double x){ // Fixed in response to comment: capture by value
return std::sqrt(dsSquared);
};
});
dydx(1.0); // efficient evaluation of cubicSplineFunction's derivative
dsdx(2.0); // efficient evaluation of cubicSplineFunction's arc rate
Итак, я реализовал классы ниже. Но каким типом я должен заменить T
(в строке 8) выше, чтобы обозначить «что-то вызываемое с подписью double(double)
»?
template<typename S>
struct SplineFunction {
std::vector<S> splines;
auto operator()(double t) const {
int i = static_cast<int>(t);
return splines[i](t - i);
}
template<typename R, typename F>
SplineFunction <R> transform(F f) const {
SplineFunction <R> tfs;
for (const auto& s : splines) {
tfs.splines.push_back(f(s));
}
return tfs;
}
// ... MORE CODE ...
}
template<int N>
struct Polynomial {
std::array<double, N+1> coeffs;
double operator()(double x) const;
Polynomial<N - 1> derivative() const;
// ... MORE CODE ...
}
template<int L, int M>
Polynomial<L+M> operator*(const Polynomial<L>& lhs, const Polynomial<M>& rhs);
template<int L>
Polynomial<L> operator+(Polynomial<L> lhs, double rhs);
// ... MORE CODE ...
"что-то вызываемое с подписью double(double)
" можно записать как std::function<double(double)>
Другая возможность - полностью отбросить параметр R
и определить тип возвращаемого значения: template<typename F> auto transform(F f) const -> SplineFunction<decltype(f(std::declval<S>()))> { ... }
@IgorTandetnik Вы можете написать это с помощью std::function
, но только с накладными расходами (как по производительности, так и по размеру). В данном случае это не имеет значения, но стоит отметить.
template<class F, class R=std::result_of_t<F&(S const&)>>
SplineFunction<R> transform(F f) const
не передавайте типы явно; пусть они будут выведены.
В C++ 11 делаем typename std::result_of<F&(S const&)>::type
.
Распад типа R (как в std decay) также может быть разумным, поскольку SplineFunction хранит свой параметр шаблона, а decay делает типы более подходящими для хранения.
В C++ 17 std::result_of
устарел.
@daniel да; но причина в том, что его трудно использовать; Я правильно использовал. Версия C++ 2a / b - это механическое преобразование.
Я не говорю, что вы использовали его неправильно; Я выступаю против использования устаревших функций в целом (это затрудняет обновление) и особенно против того, чтобы делать это без указания того, что они устарели. Хотя в данном случае я думаю, что и result_of
, и invoke_result
- неподходящие инструменты для работы, и если бы я был на реальном компьютере, я бы расширил Предложение Игоря Тандетника по вычету возвращаемого типа до полного ответа.
Опасно, Уилл Робинсон! Второй вызов
transform
создает лямбду, которая захватывает локальную переменную по ссылке, а затем возвращает эту лямбду вызывающей стороне. Вызывающий получает лямбду, содержащую висящую ссылку.