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

How to write CPP template functions that is recursive on argument array length

假设我想编写一个函数 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 的情况下终止,并且会超过其最大深度。

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

我通过定义一个新的 class 编码长度信息得出了一个解决方案。但是我不知道这是不是最优雅的方式

template<size_t N>
struct intClass{};

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

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

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

你被参数衰减咬伤了。

参数衰减意味着 int arr[N]int* arr 的花言巧语。 N 被完全忽略。

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

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

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 的好理由。当编译器更新时,UB 可能会在 10 年后回来咬我们,我不想在每次编译器更新时都维护此代码并确保它仍然有效。

真的,使用打包和折叠。

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, typename T>
void arrfill(T (&arr)[N], T v){
  arrfill(arr, v, std::make_index_sequence<N>{});
}

或者只使用 std::fill_n.

template<size_t N, typename T>
void arrfill(T (&arr)[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* arr, 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);
}

这让我们 select 使用重载的 0 案例。我们也自动推导出N