如何编写在参数数组长度上递归的 CPP 模板函数

问题描述

假设我想编写一个函数 arrfill<N> 来填充长度为 N 的数组。下面是我试过的模板实现。

template<typename T>
bool arrfill(T arr[0],T v){;}

template<size_t N,typename T>
void arrfill(T arr[N],T v){
    arr[0] = v;
    arrfill<N-1>(arr+1,v);
}

int main(int argc,char const *argv[])
{
    bool barr[4];
    arrfill<4>(barr,true);
}

但是,这不会编译,因为模板实例化不会在 N0 的情况下终止,并且会超过其最大深度

似乎编译器不会将签名中的数组大小作为参数类型。我想知道指定它的正确方法是什么?

解决方法

你被论证的衰落咬了。

参数衰减意味着 int arr[N]int* arr 的奇谈。 N 被完全忽略。

最重要的是,arrfill<N-1> 是对第一个参数是 size_t(或兼容)的模板函数的调用。您的 T arr[0] 是一个重载,它将一个类型作为它的第一个模板参数。所以无法选择。

要解决参数衰减问题,您应该通过引用获取数组。

template<typename T>
bool arrfill(T (&arr)[1],T v){arr[0]=v;}

template<size_t N,typename T>
void arrfill(T (&arr)[N],T v){
  arr[0] = v;
  arrfill(*reinterpret_cast<T(*)[N-1]>(arr+1),v);
}

遗憾的是,这是未定义的行为;我正在将数组的一部分转换为它不是的类型的数组。这恰好是未定义的行为,我曾经使用过的每个 C++ 编译器都会使用它并做“正确的事情”,但它仍然是未定义的行为。我们应该避免这种情况,除非我们有充分的理由不这样做;虽然干净和清晰,但干净的代码不是(在我看来)做 UB 的好理由。 10 年后编译器更新时,UB 可能会回来咬我们,我不想在每次编译器更新时都维护此代码并确保它仍然有效。

真的,使用包装和折叠。

template<size_t N,typename T,std::size_t...Is>
void arrfill(T (&arr)[N],T v,std::index_sequence<Is...>){
  ((void)(arr[Is]=v),...);
}
template<size_t N,T v){
  arrfill(arr,v,std::make_index_sequence<N>{});
}

或者只使用 std::fill_n

template<size_t N,T v){
  std::fill_n( std::begin(arr),N,v );
}

如果你真的,真的必须使用递归

template<size_t N,typename T>
void arrfill(T* arr,T v){
  if constexpr(N==0) {
    return;
  } else {
    arr[0] = v;
    arrfill<N-1>(arr+1,v);
  }
}

做到了。在 中,我们不能使用 if constexpr。所以我们做别的事情。

template<typename T>
void arrfill(std::integral_constant<std::size_t,0>,T* arr,T const& v){
}

template<size_t N,typename T>
void arrfill(std::integral_constant<std::size_t,N>,T const& v){
  arr[0] = v;
  arrfill(std::integral_constant<std::size_t,N-1>{},arr+1,v);
}

template<size_t N,typename T>
void arrfill(T(&arr)[N],T const& v){
  arrFill(std::integral_constant<std::size_t,N>{},arr,v);
}

这让我们可以使用重载来选择 0 情况。我们还会自动推导出 N

,

我提出了一个解决方案,定义了一个新的编码长度信息的类。但我想知道这是否是最优雅的方式。

template<size_t N>
struct intClass{};

template<typename T>
bool arrfill(T arr[0],intClass<0>){;}

template<size_t N,typename T>
void arrfill(T arr[N],intClass<N>){
    arr[0] = v;
    arrfill(arr+1,intClass<N-1>());
}

template<size_t N,T v){
    arrfill(arr,intClass<N>());
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...