0

假设我想编写一个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);
}

但是,这不会编译,因为模板实例化不会在何时终止N并且0将超过其最大深度。

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

4

2 回答 2

2

你被争论的衰败所困扰。

论据衰减意味着那int arr[N]是花哨的谈话int* arrN完全无视。

最重要的是,arrfill<N-1>调用模板函数,其第一个参数是 size_t (或兼容)。YourT 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, 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);
}

这让我们可以使用重载选择 0 情况。我们也自动推断N

于 2021-04-21T03:19:12.427 回答
1

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

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>());
}
于 2021-04-21T02:55:05.003 回答