函数族是函数的可数列表
y=f_i(x)
,对于某个整数 n 的计数器 i=0,...,n-1。
我在 C++ 中有一个派生结构,它将为
n=3
定义这样一个函数族:
template<typename Tfloat, size_t n> Base{
template<size_t i> bool f(Tfloat* x){ static_assert(i<n); return false; }
};
template<typename Tfloat>
struct Derived: Base<Tfloat,3>{
template<> bool f<0>(Tfloat* x){ x[3]-=x[2]; return true; }
template<> bool f<1>(Tfloat* x){ x[1]*=x[3]; return true; }
template<> bool f<2>(Tfloat* x){ if(x[5]==0) return false; x[2]/=x[5]; return true; }
};
我怎样才能实现以下目标?:
std::sequence<n>
。到目前为止,我已经给每个函数起了不同的名称f0, f1,...
。但随后必须手动编写一个 if constexpr(i=0){ return f0(x); }
包装器来遍历 template<size_t i> f(Tfloat* x)
定义中的所有名称。事实上,这就是我现在正在做的事情。我知道有很多问题:
f<i>
进行 i>=n
的调用,但我认为没有办法强制执行所有 i=0,...,n-1
的定义。因此我很好奇你的想法。
i
的功能彼此之间几乎没有什么关系;例如,火箭第 i
级的物理模型。我更喜欢使用模板 size_t i
。摘自@HolyBlackCat的评论:
#include<iostream>
#include<functional>
#include<array>
template<typename Tfloat, size_t n>
class Base{
protected:
std::array<std::function<bool(Tfloat*)>,n> f_;
public:
// probably here in conjunction with an invalid default initialize for each element of f_, CRTP could be used to assert after construction of derived that each f_[i] is defined?
template<size_t i>
bool f(Tfloat* x){ return f_[i](x); } // accessor prevents external overwrite into f_.
};
template<typename Tfloat>
class Derived: public Base<Tfloat,3>{
public:
//
Derived(){
Base<Tfloat,3>::f_[0] = [this](Tfloat* x){
x[0] *= x[1];
return true;
};
}
//
};
template<typename Tfloat>
struct NaiveDerived{
//
bool f0(Tfloat* x){
x[0] *= x[1];
return true;
}
//
};
int main(){
Derived<float> d;
float x[10];
d.f[0](x);
}
此代码有效并实现了要求 2 和 3。
我不太喜欢使用函数数组,因为最终我们想要结合两个东西:
{ x[0]*=x[1]; return true; }
f<i>(x)
将
f_
引入混合中只会避免在将每个函数体分配给其各自的 f<i>
之前给每个函数体命名。更具可读性的解决方案是直接用 i
来说明第 f<i>
函数体。
检查这个:
template <std::size_t n>
using index_constant = std::integral_constant<std::size_t, n>;
template<typename arg, std::size_t m>
struct base{
template<std::size_t n>
requires (n < m)
bool f(this auto& self, index_constant<n> i, arg val){ return self.f_impl(i, std::forward<arg>(val));
~base(this auto& self){
[&]<std::size_t...i>(std::index_sequence<i...>)
{static_assert((
std::is_same_v<
bool,
decltype(self.f_impl(index_constant<i>{}, std::declval<arg>()))
>&&...
));} (std::make_index_sequence<m>{});
};
};
struct derived: base<float, 3>{
template<std::size_t n>
bool f_impl(index_constant<n>, float){ return true; };
};
/*...*/
derived d;
bool flag=[&d]<std::size_t ...i>(std::index_sequence<i...>)
{return (d.f(index_constant<i>,1.0f) && ...);}
(std::make_index_sequence<3>{});
在
base
类中,我使用了 C++ 导出的 this
功能来简化 CRTP,并避免了导出的冗长用法。您可以通过折叠表达式和通用 lambda 来摆脱索引的编译时遍历,如上面的示例所示。