cpp中函数族的最佳实践实现

问题描述 投票:0回答:1

初步

函数族是函数的可数列表

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; }
};

问题

我怎样才能实现以下目标?:

  1. 在编译时通过从 Base 继承强制定义该族的所有三个函数。
  2. 我需要能够静态地循环 f ,例如一个
    std::sequence<n>
    。到目前为止,我已经给每个函数起了不同的名称
    f0, f1,...
    。但随后必须手动编写一个
    if constexpr(i=0){ return f0(x); }
    包装器来遍历
    template<size_t i> f(Tfloat* x)
    定义中的所有名称。事实上,这就是我现在正在做的事情。
  3. 能够在 Derived 主体中编写定义。

问题

我知道有很多问题:

  • 不能在 Derived 范围内专门化模板函数。
  • 甚至根本无法特化非特化 Derived 的模板函数。
  • 虽然可以静态禁止对
    f<i>
    进行
    i>=n
    的调用,但我认为没有办法强制执行所有
    i=0,...,n-1
    的定义。

因此我很好奇你的想法。

备注

  • 通常,不同
    i
    的功能彼此之间几乎没有什么关系;例如,火箭第
    i
    级的物理模型。我更喜欢使用模板
    size_t i
  • 派生通常保存运行时相关参数以及从这些参数计算/初始化的数据。因此,使用命名空间而不是 Derived 结构对我来说似乎不适合。

提案:函数数组

摘自@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。

我不太喜欢使用函数数组,因为最终我们想要结合两个东西:

  • 第 i 个函数的函数体
    { x[0]*=x[1]; return true; }
  • 第 i 个函数的模板函数名称
    f<i>(x)

f_
引入混合中只会避免在将每个函数体分配给其各自的
f<i>
之前给每个函数体命名。更具可读性的解决方案是直接用
i
来说明第
f<i>
函数体。

c++ template-specialization static-assert
1个回答
0
投票

检查这个:

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 来摆脱索引的编译时遍历,如上面的示例所示。

© www.soinside.com 2019 - 2024. All rights reserved.