如何为支持任意维度函数积分的C++积分器类编写Concept
我正在为一个库编写数值积分器。数值积分器有多种类型(牛顿-科茨法、高斯求积法等),我希望这些积分器可以互换使用。通常的做法是定义一个Integrator接口,再由具体类型实现该接口。但我的积分器需要支持对任意算术类型的函数进行积分(这也是我自行实现的原因之一),即不仅要支持double,还要支持DoubleDouble以及任何表现合理的实数表示类型。因此,我决定将积分器实现为模板类,所有需要积分器的函数也设为模板函数,将积分器作为模板参数。不过,我希望用某个Integrator concept来约束这个模板参数。
#include <iostream> #include <array> #include <type_traits> #include <functional> template<typename F, typename Ret, typename A, typename... Rest> A helper(Ret(F::*)(A, Rest...)); template<typename F, typename Ret, typename A, typename... Rest> A helper(Ret(F::*)(A, Rest...) const); template<typename F> struct GetFirstArgumentOf { typedef std::remove_cvref_t<decltype(helper(&F::operator()))> type; }; template<typename F> using GetReturnTypeOf = typename decltype(std::function{ std::declval<F>() })::result_type; template<typename F, typename T> concept IsFunctionFromR = requires(GetFirstArgumentOf<F>::type argument) { [](std::array<T, 1>) {}(argument); //checks argument type (TODO should work for copy or const reference) }; template<typename F, typename T> concept IsFunctionToR = requires(GetReturnTypeOf<F> return_) { [](T) {}((T) return_); }; template<typename F, typename T> concept IsFunctionFromRN = requires(GetFirstArgumentOf<F>::type argument) { []<std::size_t N>(std::array<T, N>) {}(argument); }; template<typename F, typename T> concept IsFunctionFromRToR = IsFunctionFromR<F, T> && IsFunctionToR<F, T>; template<typename F, typename T> concept IsFunctionFromRNToR = IsFunctionFromRN<F, T> && IsFunctionToR<F, T>; template<typename T = double> class Integrator { public: template<typename F> requires IsFunctionFromRToR<F, T> T Integrate(const F& f, std::array<T, 1> a, std::array<T, 1> b) const { return (b[0] - a[0]) * (f(a) + f(b)) / 2.0; } template<typename F> requires IsFunctionFromRNToR<F, T> && (!IsFunctionFromRToR<F, T>) auto Integrate(const F& f, std::array<T, 1> a, std::array<T, 1> b) const { constexpr int dimension = std::tuple_size<typename GetFirstArgumentOf<F>::type>::value; return [=](std::array<T, dimension - 1> y) { auto f_at_y = [&](std::array<T, 1> x) { std::array<T, dimension> argument{0}; argument[0] = x[0]; for(int i = 1; i < dimension; i++) { argument[i] = y[i - 1]; } return f(argument); }; return Integrate(f_at_y, a, b); }; } }; int main() { Integrator<double> i; auto f = [](std::array<double, 2> x) { return x[0] + x[1] * x[1]; }; std::array<double, 1> a = { 0 }; std::array<double, 1> b = { 1 }; std::cout << i.Integrate(i.Integrate(f, a, b), a, b) << std::endl; return 0; }
Integrator类包含两个Integrate方法:一个用于单变量函数(参数为std::array<T, 1>),返回T类型标量;另一个用于N变量函数,返回一个N-1变量的函数,对第一个变量进行积分。
顺便说明,必须使用编译期大小已知的容器(本文中为std::array,实际代码中我使用了按大小N模板化的类)。
我想了解的是,是否可以为这类积分器编写一个concept,以便我能写出如下代码:
template<IntegratorConcept I> void DoSomethingWithIntegrator(I integrator);
我知道可以用typename I替代IntegratorConcept I,只要类型I提供必要接口就能正常工作,但我想知道是否可以通过concept更明确地定义该接口。
我了解到无法创建模板化的接口(即纯虚类),而这原本能完美解决我的问题。
我找到的最接近的思路是一篇博客文章,它尝试在requires子句中使用另一个concept(但不可行),然后引入了原型的思路。不过要实现这一点,我需要为每个维度的n维函数创建无限多个原型,这显然不现实。
提前感谢各位的解答。
内容的提问来源于stack exchange,提问作者user1288851

