编译时间sizeof_array而不使用macros
这只是在过去几天困扰我的东西,我不认为有可能解决,但我以前见过模板魔术。
开始:
要获取标准C ++数组中的元素数量,我可以使用macros(1)或types安全内联函数(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])); }
正如你所看到的,第一个问题是macros(当前我认为是一个问题),另一个问题是编译时不能获取数组的大小。 即我不能写:
enum ENUM{N=sizeof_array(ARRAY)};
要么
BOOST_STATIC_ASSERT(sizeof_array(ARRAY)==10);// Assuming the size 10..
有谁知道这是否可以解决?
更新 :
这个问题是在constexpr被引入之前创build的。 现在你可以简单地使用:
template <typename T> constexpr auto sizeof_array(const T& iarray) { return (sizeof(iarray) / sizeof(iarray[0])); }
从这里尝试以下内容:
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); }
它应该打印出:“arrays数:10”
在C ++ 1x中, constexpr
会给你:
template <typename T, size_t N> constexpr size_t countof(T(&)[N]) { return N; }
我能想到的最好的是这样的:
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 (eg 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; }
[编辑]
想一想,我相信除了macros之外,这在C ++ 03中的单一“函数式调用”中是不可能的,原因如下。
一方面,您将显然需要模板参数推导来获取数组的大小(直接或通过sizeof
)。 但是,模板参数推导只适用于函数,而不适用于类; 也就是说,你可以有一个模板参数R,其types为N的参考数组,其中N是另一个模板参数,但是你必须在调用时提供R和N; 如果你想从R推导出N,只有一个函数调用可以做到这一点。
另一方面,涉及函数调用的任何expression式的唯一方式可以是常量,当它在sizeof
。 其他任何东西(例如访问函数的返回值的静态或枚举成员)仍然需要发生函数调用,这显然意味着这不会是一个常量expression式。
问题
我喜欢Adisak的回答 :
template <typename T, size_t N> char ( &_ArraySizeHelper( T (&arr)[N] ))[N]; #define COUNTOF( arr ) (sizeof( _ArraySizeHelper( arr ) ))
这是微软在VS2008中为_countofmacros使用的,它有一些很好的function:
- 它在编译时运行
- 它是types安全的 (即,如果你给它一个指针,它会产生一个编译时错误,哪个数组也会太容易退化)
但是正如Georg指出的那样,这种方法使用了模板,所以不能保证使用C ++ 03的本地types :
void i_am_a_banana() { struct { int i; } arr[10]; std::cout << COUNTOF(arr) << std::endl; // forbidden in C++03 }
幸运的是,我们并没有走运。
解决scheme
伊万·约翰逊提出了一个聪明的方法 ,赢得所有帐户:这是types安全的,编译时间,并与地方types的作品:
#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的数组大小macros之前插入两个“testing”来工作。 这些testing不会影响最终的计算,但是被devise为为非数组types生成编译错误:
- 第一个testing失败,除非
arr
是整数,枚举,指针或数组。reinterpret_cast<const T*>
对其他任何types都会失败。 -
第二个testing失败的积分,枚举或指针types。
整型和枚举types将失败,因为它们没有匹配的
check_type
版本,因为check_type
需要指针。指针types将失败,因为它们将匹配
check_type
的模板化版本,但是模板化check_type
的返回types(Is_pointer
)不完整,将会产生错误。数组types将通过,因为采用types
T
数组的地址会给你T (*)[]
,也就是指向数组的指针,而不是指针指针。 这意味着check_type
的模板版本不匹配。 感谢SFINAE ,编译器将转向非模板版本的check_type
,它应该接受任何一对指针。 由于非模板版本的返回types是完全定义的,因此不会产生错误。 而由于我们现在没有处理模板,本地types工作正常。
这不是你正在寻找的东西,但是它很接近 – 一个来自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()
macros最终用于更易读的ARRAYSIZE()
macros。
马修·威尔逊(Matthew Wilson)的“不完美的C ++”(Imperfect C ++)一书也对这里使用的技术进行了讨论。
如果您只使用微软平台,则可以利用_countofmacros。 这是一个非标准的扩展,它将返回一个数组中元素的数量。 如果在非数组types上使用它,将会导致编译错误。
下面的工作很好(VS 2008 RTM)
static int ARRAY[5]; enum ENUM{N=_countof(ARRAY)};
但再一次,这是MS的具体,所以这可能不适合你。
你不能一般解决这个问题,这就是arrays封装像boost数组的原因(当然还有stl-style的行为)。
看来不可能获得sizeof数组作为编译时常量没有当前C ++标准的macros(你需要一个函数来推导数组的大小,但函数调用是不允许的,如果你需要一个编译时常量) 。 [编辑:但看到米娜夫的辉煌的解决scheme!]
然而,你的模板版本也不是types安全的,并且会遇到和macros一样的问题:它也接受指针,尤其是接受指针的数组。 当它接受一个指针时,sizeof(T *)/ sizeof(T)的结果不可能是有意义的。
更好:
template <typename T, size_t N> size_t sizeof_array(T (&)[N]){ return N; }
没有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; }
它可以给你一个你可以传递variables的函数,或者你也可以传递这个types的模板。 您不能使用该函数编译时间常量,但大多数情况下您知道该types,即使只作为模板参数。
现在,STL库可以决定/select数组大小的编译时间
#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