不使用宏编译时间sizeof_array

Vik*_*ehr 16 c++ puzzle metaprogramming

这只是过去几天困扰我的事情,我认为不可能解决,但我之前看过模板魔术.

开始:

为了获得标准C++数组中的元素数量,我可以使用宏(1)或类型安全的内联函数(2):

(1)

#define sizeof_array(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))
Run Code Online (Sandbox Code Playgroud)

(2)

template <typename T>
size_t sizeof_array(const T& ARRAY){
    return (sizeof(ARRAY)/sizeof(ARRAY[0]));
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,第一个问题是成为一个宏(目前我认为是一个问题),而另一个问题是在编译时无法获得数组的大小; 即我不能写:

enum ENUM{N=sizeof_array(ARRAY)};
Run Code Online (Sandbox Code Playgroud)

要么

BOOST_STATIC_ASSERT(sizeof_array(ARRAY)==10);// Assuming the size 10..
Run Code Online (Sandbox Code Playgroud)

有谁知道这是否可以解决?

更新:

这个问题是在引入constexpr之前创建的.现在你可以简单地使用:

template <typename T>
constexpr auto sizeof_array(const T& iarray) {
    return (sizeof(iarray) / sizeof(iarray[0]));
}
Run Code Online (Sandbox Code Playgroud)

Adi*_*sak 22

这里尝试以下内容:

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);
}
Run Code Online (Sandbox Code Playgroud)

它应该打印出来:"数组计数是:10"

  • +1.这具有类型安全的额外好处(即,如果传递指针而不是数组,它将无法编译). (3认同)
  • 它是一个很好的解决方案,但不完全通用:"它不适用于函数内定义的类型" (3认同)
  • @gf:对于函数内定义的类型,模板通常不能很好地工作.实际上,它不适用于STL - 以下内容不能在GCC下编译:void test(){struct foo {int a; }; std :: vector <foo> b; 如果您需要创建一个仅由一个函数使用的类并且您需要在该类上使用模板,那么使用命名空间(或匿名命名空间)会好得多. (2认同)

dal*_*lle 18

在C++中,1x constexpr将为您提供:

template <typename T, size_t N>
constexpr size_t countof(T(&)[N])
{
    return N;
}
Run Code Online (Sandbox Code Playgroud)

  • 但是在C++ 98中,它不能用于需要常量表达式的地方 (5认同)

Pav*_*aev 8

我能想到的最好的是:

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];
Run Code Online (Sandbox Code Playgroud)

必须与另一个一起使用sizeof:

int main()
{
    int a[10];
    int n = sizeof(sizeof_array(a));
    std::cout << n << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

[编辑]

想一想,我相信除了宏之外,在C++ 03中单独的"类似函数的调用"这是不可能做到的,这就是原因.

一方面,您显然需要模板参数推导来获得数组的大小(直接或通过sizeof您的方式).但是,模板参数推导仅适用于函数,而不适用于类; 即你可以有一个类型为reference-to-array-of-N的模板参数R,其中N是另一个模板参数,但是你必须在调用点提供R和N. 如果你想从R中推导出N,那么只有函数调用才能做到.

另一方面,涉及函数调用的任何表达式都可以是常量的唯一方法是它在内部时sizeof.其他任何东西(例如,在函数的返回值上访问静态或枚举成员)仍然需要进行函数调用,这显然意味着这不是常量表达式.


Nat*_*ohl 6

问题

我喜欢Adisak的回答:

template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&arr)[N] ))[N];
#define COUNTOF( arr ) (sizeof( _ArraySizeHelper( arr ) ))
Run Code Online (Sandbox Code Playgroud)

这是微软在VS2008中用于_countof宏的东西,它有一些不错的功能:

  • 它在编译时运行
  • 它是类型安全的(即如果你给它一个指针,它将产生编译时错误,这些数组也很容易降级)

但正如Georg指出的那样,这种方法使用模板,所以不保证可以使用C++ 03的本地类型:

void i_am_a_banana() {
  struct { int i; } arr[10];
  std::cout << COUNTOF(arr) << std::endl; // forbidden in C++03
}
Run Code Online (Sandbox Code Playgroud)

幸运的是,我们没有运气.

解决方案

Ivan Johnson提出了一个聪明的方法,赢得所有帐户:它是类型安全的,编译时,并使用本地类型:

#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*);
};
Run Code Online (Sandbox Code Playgroud)

对于那些感兴趣的人,它通过在标准的基于sizeof的数组大小宏之前插入两个"测试"来工作.这些测试不影响最终计算,但旨在为非数组类型生成编译错误:

  1. 除非arr是整数,枚举,指针或数组,否则第一个测试失败. reinterpret_cast<const T*>任何其他类型都应该失败.
  2. 对于整数,枚举或指针类型,第二次测试失败.

    积分和枚举类型将失败,因为它们没有check_type匹配的版本,因为check_type需要指针.

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

    数组类型将通过,因为获取类型数组的地址T 将为您提供T (*)[],也就是指向数组的指针,而不是指向指针的指针.这意味着模板版本check_type将不匹配.感谢SFINAE,编译器将继续使用非模板化版本check_type,它应该接受任何一对指针.由于完全定义了非模板化版本的返回类型,因此不会产生错误.由于我们现在不处理模板,因此本地类型工作正常.


Mic*_*urr 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)))
Run Code Online (Sandbox Code Playgroud)

RTL_NUMBER_OF_V2()宏最终在更可读的正在使用ARRAYSIZE()的宏.

马修威尔逊的"不完美的C++"一书也讨论了这里使用的技术.

  • 我喜欢评论的最后一行:"这就是重点." (2认同)