这是过去几天困扰我的事情,我认为不可能解决,但我之前看过模板魔术。
这里是:
要获取标准C ++数组中的元素数量,我可以使用宏(1)或类型安全内联函数(2):
(1)
#define sizeof_array(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))
(2)
template <typename T>
size_t sizeof_array(const T& ARRAY){
return (sizeof(ARRAY)/sizeof(ARRAY[0]));
}
正如您所看到的,第一个问题是作为一个宏(目前我认为是一个问题),而另一个问题是在编译时无法获得数组的大小;即我不能写:
enum ENUM{N=sizeof_array(ARRAY)};
或
BOOST_STATIC_ASSERT(sizeof_array(ARRAY)==10);// Assuming the size 10..
有谁知道这是否可以解决?
更新 :
这个问题是在引入constexpr之前创建的。现在你可以简单地使用:
template <typename T>
constexpr auto sizeof_array(const T& iarray) {
return (sizeof(iarray) / sizeof(iarray[0]));
}
答案 0 :(得分:21)
从here:
中尝试以下操作template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&array)[N] ))[N];
#define mycountof( array ) (sizeof( _ArraySizeHelper( array ) ))
int testarray[10];
enum { testsize = mycountof(testarray) };
void test() {
printf("The array count is: %d\n", testsize);
}
应打印出:“数组计数为:10”
答案 1 :(得分:15)
在C ++中,1x constexpr
将为您提供:
template <typename T, size_t N>
constexpr size_t countof(T(&)[N])
{
return N;
}
答案 2 :(得分:7)
我能想到的最好的是:
template <class T, std::size_t N>
char (&sizeof_array(T (&a)[N]))[N];
// As litb noted in comments, you need this overload to handle array rvalues
// correctly (e.g. when array is a member of a struct returned from function),
// since they won't bind to non-const reference in the overload above.
template <class T, std::size_t N>
char (&sizeof_array(const T (&a)[N]))[N];
必须与另一个sizeof
:
int main()
{
int a[10];
int n = sizeof(sizeof_array(a));
std::cout << n << std::endl;
}
<强> [编辑] 强>
考虑到这一点,我相信除了宏之外,在C ++ 03的单个“类似函数的调用”中这是不可能做到的,这就是原因。
一方面,您显然需要模板参数推导来获得数组的大小(直接或通过sizeof
获得)。但是,模板参数推导仅适用于函数,而不适用于类;即你可以有一个类型为reference-to-array-of-N的模板参数R,其中N是另一个模板参数,但是你必须在调用点提供R和N.如果你想从R中推导N,只有函数调用可以做到。
另一方面,任何涉及函数调用的表达式都可以是常量的唯一方法是它在sizeof
内。其他任何事情(例如,在函数的返回值上访问静态或枚举成员)仍然需要进行函数调用,这显然意味着这不是常量表达式。
答案 3 :(得分:6)
我喜欢Adisak's answer:
template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&arr)[N] ))[N];
#define COUNTOF( arr ) (sizeof( _ArraySizeHelper( arr ) ))
这是微软在VS2008中用于_countof macro的内容,它有一些不错的功能:
但正如Georg所指出的,此方法使用模板,因此它是not guaranteed to work with local types for C++03:
void i_am_a_banana() {
struct { int i; } arr[10];
std::cout << COUNTOF(arr) << std::endl; // forbidden in C++03
}
幸运的是,我们没有运气。
Ivan Johnson提出a clever approach赢得所有帐户:它是类型安全的,编译时,并且适用于本地类型:
#define COUNTOF(arr) ( \
0 * sizeof(reinterpret_cast<const ::Bad_arg_to_COUNTOF*>(arr)) + \
0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + \
sizeof(arr) / sizeof((arr)[0]) )
struct Bad_arg_to_COUNTOF {
class Is_pointer; // incomplete
class Is_array {};
template <typename T>
static Is_pointer check_type(const T*, const T* const*);
static Is_array check_type(const void*, const void*);
};
对于那些感兴趣的人,它通过在标准的基于sizeof的数组大小宏之前插入两个“测试”来工作。这些测试不影响最终计算,但旨在为非数组类型生成编译错误:
arr
是整数,枚举,指针或数组,否则第一次测试失败。任何其他类型的reinterpret_cast<const T*>
都会失败。对于整数,枚举或指针类型,第二次测试失败。
积分和枚举类型将失败,因为它们匹配的check_type
版本没有,因为check_type
需要指针。
指针类型将失败,因为它们将匹配check_type
的模板化版本,但模板化Is_pointer
的返回类型(check_type
)不完整,这将产生错误。
数组类型将通过,因为获取类型为T
的数组的地址
会给你T (*)[]
,也就是指向数组的指针,而不是指向指针的指针。这意味着check_type
的模板版本将不匹配。感谢SFINAE,编译器将继续使用check_type
的非模板化版本,它应该接受任何一对指针。由于完全定义了非模板化版本的返回类型,因此不会产生错误。由于我们现在不处理模板,因此本地类型工作正常。
答案 4 :(得分:5)
这不完全是你想要的,但它很接近 - 来自winnt.h
的片段,其中包含对#$%^正在做什么的一些解释:
//
// RtlpNumberOf is a function that takes a reference to an array of N Ts.
//
// typedef T array_of_T[N];
// typedef array_of_T &reference_to_array_of_T;
//
// RtlpNumberOf returns a pointer to an array of N chars.
// We could return a reference instead of a pointer but older compilers do not accept that.
//
// typedef char array_of_char[N];
// typedef array_of_char *pointer_to_array_of_char;
//
// sizeof(array_of_char) == N
// sizeof(*pointer_to_array_of_char) == N
//
// pointer_to_array_of_char RtlpNumberOf(reference_to_array_of_T);
//
// We never even call RtlpNumberOf, we just take the size of dereferencing its return type.
// We do not even implement RtlpNumberOf, we just decare it.
//
// Attempts to pass pointers instead of arrays to this macro result in compile time errors.
// That is the point.
//
extern "C++" // templates cannot be declared to have 'C' linkage
template <typename T, size_t N>
char (*RtlpNumberOf( UNALIGNED T (&)[N] ))[N];
#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A)))
RTL_NUMBER_OF_V2()
宏最终会用于更具可读性的ARRAYSIZE()
宏。
Matthew Wilson's "Imperfect C++"书中还讨论了这里使用的技术。
答案 5 :(得分:4)
如果您使用的是Microsoft平台,则可以利用_countof宏。这是一个非标准扩展,它将返回数组中元素的数量。它比大多数countof样式宏的优点是,如果它在非数组类型上使用,它将导致编译错误。
以下工作正常(VS 2008 RTM)
static int ARRAY[5];
enum ENUM{N=_countof(ARRAY)};
但是再一次,它是MS特定的,所以这可能不适合你。
答案 6 :(得分:3)
你通常无法解决它,这就是数组包装器的原因之一,如boost array(当然还有stl风格的行为)。
答案 7 :(得分:2)
似乎无法在没有具有当前C ++标准的宏的情况下将sizeof数组作为编译时常量获取(您需要一个函数来推导数组大小,但是在您需要编译的情况下不允许函数调用 - 时间常数)。 [编辑:但看看Minaev的精彩解决方案!]
但是,您的模板版本也不是类型安全的,并且遇到与宏相同的问题:它还接受指针,特别是数组衰减为指针。当它接受指针时,sizeof(T *)/ sizeof(T)的结果无意义。
更好:
template <typename T, size_t N>
size_t sizeof_array(T (&)[N]){
return N;
}
答案 8 :(得分:2)
没有C ++ 0x,我能得到的最接近的是:
#include <iostream>
template <typename T>
struct count_of_type
{
};
template <typename T, unsigned N>
struct count_of_type<T[N]>
{
enum { value = N };
};
template <typename T, unsigned N>
unsigned count_of ( const T (&) [N] )
{
return N;
};
int main ()
{
std::cout << count_of_type<int[20]>::value << std::endl;
std::cout << count_of_type<char[42]>::value << std::endl;
// std::cout << count_of_type<char*>::value << std::endl; // compile error
int foo[1234];
std::cout << count_of(foo) << std::endl;
const char* bar = "wibble";
// std::cout << count_of( bar ) << std::endl; // compile error
enum E1 { N = count_of_type<int[1234]>::value } ;
return 0;
}
可以为您提供可以传递变量的函数,也可以为您传递类型的模板。您不能将该函数用于编译时常量,但大多数情况下您知道该类型,即使仅作为模板参数。
答案 9 :(得分:0)
现在可以使用STL库来决定/选择数组大小编译时间
#include <iostream>
#include <array>
template<class T>
void test(T t)
{
int a[std::tuple_size<T>::value]; // can be used at compile time
std::cout << std::tuple_size<T>::value << '\n';
}
int main()
{
std::array<float, 3> arr;
test(arr);
}
输出: 3