如何在编译期间计算数组大小(不接受指针)

How compute array size during compilation (without accepting pointers)?

本文关键字:不接受 指针 数组 计算 编译      更新时间:2023-10-16

给定一个数组a,我希望countof(a)将数组中的元素数量作为编译时常数。如果我有一个指针p,我希望countof(p)不编译。这似乎应该是(1)简单明了,(2)通常在SO中涵盖,但(1)我无法让它工作,(2,搜索SO没有找到任何结果。

这是我的尝试。

#include <cstddef>
#include <type_traits>
template<typename T, std::size_t n,
typename = typename std::enable_if<std::is_array<T>::value>::type>
constexpr std::size_t countof(T (&)[n]) { return n; }
template<typename T, 
typename = typename std::enable_if<std::is_pointer<T>::value>::type>
void countof(T*) = delete;
int main()
{
int a[10];
auto asize = countof(a);             // should compile
static_assert(countof(a) == 10,
"countof(a) != 10!");
int *p;
auto psize = countof(p);             // shouldn't compile
}

帮助?

template<typename T, std::size_t N>
constexpr std::size_t countof( T const(&)[N] ) { return N; }

通过两项测试。无法将int*转换为T const(&)[N],因此不需要禁用代码。

为了扩展它,我们应该添加:

template<typename T, std::size_t N>
constexpr std::size_t countof( std::array<T,N> const& ) { return N; }

我甚至可能想将其扩展为对容器调用size()。虽然通常不会是编译时,但统一性可能很有用:

for(int i=0; i<countof(c); ++i) {
// code
}

或者你有什么。

template<typename T, std::size_t N>
constexpr std::size_t countof( T const(&)[N] ) { return N; }
template<typename T> struct type_sink { typedef void type; };
template<typename T> using TypeSink = typename type_sink<T>::type;
template<typename T, typename=void>
struct has_size : std::false_type {};
template<typename T>
struct has_size<T, TypeSink< decltype( std::declval<T>().size() ) > >:
std::true_type
{};
template<bool b, typename T=void>
using EnableIf = typename std::enable_if<b,T>::type;
template<typename T>
constexpr
EnableIf<has_size<T const&>::value,std::size_t>
countof( T const& t ) {
return t.size();
}
// This is optional.  It returns `void`, because there
// is no need to pretend it returns `std::size_t`:
template<typename T>
constexpr
EnableIf<std::is_pointer<T>::value>
countof( T const& t ) = delete;

这非常详细,但为我们提供了std::array支持、std::initializer_list支持、C样式数组支持——所有这些都在编译时——以及在运行时标准容器和字符串都可以使用countof。如果你传递一个指针,你会被告知你调用的函数是deleteed.

在这种情况下,我试图创建一个static_assert,但遇到了任何template都必须具有有效专门化的解析规则的问题。我怀疑通过基于SFINAE的专门化将整个问题路由到countof_impl类中可能会解决这个问题。

=deletestatic_assert解决方案的缺点是指针实际上存在过载。如果你没有,那么就没有一个有效的函数可以调用,它需要一个指针:这更接近事实。

像这样:

template <typename T, std::size_t n>
constexpr std::size_t countof(T (&)[n]) { return n; }
template <typename T, typename = typename std::enable_if<std::is_pointer<T>::value>::type>
constexpr std::size_t countof(T) = delete;

您可以这样做:

#include <iostream>
#include <type_traits>
namespace Detail {
template <typename T>
struct array_size {
// A simple false is no good
static_assert(std::is_array<T>::value, "No Array");
};
template <typename T, std::size_t N>
struct array_size<T[N]> {
static constexpr std::size_t value = N;
};
}
template <typename T>
constexpr std::size_t array_size() {
return Detail::array_size<T>::value;
}
template <typename T>
constexpr std::size_t array_size(const T&) {
return Detail::array_size<T>::value;
}
int main(){
typedef int A[3];
typedef char B[array_size<A>()];
A a;
std::cout << array_size<A>() << array_size(a) << array_size<B>() << std::endl;
// int* p = a;
// error: static assertion failed: No Array
// std::cout << array_size(p) << std::endl;
return 0;
}

如果你需要压平所有维度,这个摘录可以在手上

//Moving to detail like 'Dieter Lücking'
namespace detail {
/*recurse over ranks*/
template <typename A, size_t R = std::rank<A>::value>
struct aux {
static constexpr size_t value =
std::extent<A, 0>::value * aux<typename std::remove_extent<A>::type>::value;
};
/*stop condition*/
template <typename A>
struct aux<A, 0> {
static constexpr size_t value = 1;
};
}
/*convenient function, updated to use enable_if, is_array*/
template <typename A, typename = typename std::enable_if<std::is_array<A>::value>::type>
constexpr size_t countof(A const &) {
return detail::aux<A>::value;
}

使用示例:

int a[][3][3] = {
{{1,2,3},
{1,2,3},
{1,2,3}},
{{1,2,3},
{1,2,3},
{1,2,3}}
};
int b[countof(a)]; //size 2*3*3*1 = 18

对于那些必须在没有C++11的constexpr的情况下使用遗留C++编译器的人来说,以下方法将起作用:

#include <cstddef>
template <class T, size_t N> char (*countof(T(&)[N]))[N]; // declaration only
#define countof(x)          sizeof(*countof(x))
int main()
{
int a[10];
size_t asize = countof(a);             // should compile
static_assert(countof(a) == 10,
"countof(a) != 10!");
int *p;
size_t psize = countof(p);             // shouldn't compile
}

该技术通过在sizeof运算符中嵌入ADL来确保countof的编译时评估。