假设我想编写一个函数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
并且将超过其最大深度时,模板实例化不会终止。
编译器似乎不会将签名中的数组大小作为参数类型。我想知道具体说明的正确方式是什么?
您被参数衰减所困扰。
论证衰变意味着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的好理由。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>{});
}
或者仅仅使用CCD_ 10。
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);
}
}
在c++11中,我们不能使用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情况。我们还自动推导出CCD_ 11。
我通过定义一个编码长度信息的新类,提出了一个解决方案。但我想知道这是否是最优雅的方式。
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>());
}