编译时间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生成编译错误:

  1. 第一个testing失败,除非arr是整数,枚举,指针或数组。 reinterpret_cast<const T*>对其他任何types都会失败。
  2. 第二个testing失败的积分,枚举或指针types。

    整型和枚举types将失败,因为它们没有匹配的check_type版本,因为check_type需要指针。

    指针types将失败,因为它们将匹配check_type的模板化版本,但是模板化check_type的返回types( Is_pointer )不完整,将会产生错误。

    数组types将通过,因为采用typesT数组的地址会给你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

Interesting Posts