我正在编写数字积分器作为库的一部分。您可能知道,有很多不同类型的数值积分器(牛顿科特斯、高斯求积等),我希望它们可以互换。通常,人们可以通过拥有集成器的接口,然后拥有一些实现该接口的具体类型来实现这一点。然而,我的积分器需要能够集成任何算术类型的函数(这是我自己编写它的部分原因),即它们需要支持 double 但也支持 DoubleDouble 以及任何合理地表现为真实的东西数字表示。因此,我决定将积分器编写为模板类,并将需要积分器的任何其他函数作为模板函数,其中积分器将是模板参数。但是,我希望此模板参数受到某些集成器概念的约束。
#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 方法:一种用于 1 个变量的函数 (std::array
顺便说一句,使用在编译时已知大小的容器是不可协商的(问题中的 std::array ,在我的实际代码中,我使用大小为 N 的模板化类)
那么,我想知道是否可以为这样的积分器编写一个概念,以便我可以写:
template<IntegratorConcept I>
void DoSomethingWithIntegrator(I integrator);
我知道我可以只写“typename I”而不是“IntegratorConcept I”,并且只有当类型 I 提供必要的接口时它才会起作用,但我想知道是否可以通过概念使该接口更加明确.
我知道我无法创建一个作为模板的接口(又名纯虚拟类),这可以非常优雅地解决我的问题。
我能找到的最接近的其他想法是这篇博客文章,它开始尝试编写一个在requires子句中使用另一个概念的概念(这不起作用),然后引入原型的想法。然而,为了完成这项工作,我需要创建无限数量的原型,每个原型对应 n 维函数的每个维度。
提前感谢您的回答。
可以为这样的积分器写一个概念,这样我就可以写:...
是的。您可以从一个简单的类型特征开始:
template<class T>
struct is_integrator : std::false_type {};
template<class T>
struct is_integrator<Integrator<T>> : std::true_type {};
用于
concept
:
template<class T>
concept IntegratorConcept = is_integrator<T>::value;
您也可以直接在函数中使用
concept
:
void DoSomethingWithIntegrator(IntegratorConcept auto integrator) {
//...
}