编译时 sizeof_array 不使用宏

发布于 2024-08-06 02:27:02 字数 917 浏览 5 评论 0原文

这只是过去几天困扰我的事情,我认为不可能解决,但我以前见过模板魔法。

如下:

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

(1)

#define sizeof_array(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))

(2)< /strong>

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]));
}

This is just something that has bothered me for the last couple of days, I don't think it's possible to solve but I've seen template magic before.

Here goes:

To get the number of elements in a standard C++ array I could use either a macro (1), or a typesafe inline function (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]));
}

As you can see, the first one has the problem of being a macro (for the moment I consider that a problem) and the other one has the problem of not being able to get the size of an array at compile time; ie I can't write:

enum ENUM{N=sizeof_array(ARRAY)};

or

BOOST_STATIC_ASSERT(sizeof_array(ARRAY)==10);// Assuming the size 10..

Does anyone know if this can be solved?

Update:

This question was created before constexpr was introduced. Nowadays you can simply use:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

多彩岁月 2024-08-13 02:27:02

请尝试此处执行以下操作:

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”

Try the following from 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);
}

It should print out: "The array count is: 10"

慵挽 2024-08-13 02:27:02

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

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

In C++1x constexpr will get you that:

template <typename T, size_t N>
constexpr size_t countof(T(&)[N])
{
    return N;
}
软糖 2024-08-13 02:27:02

我能想到的最好的办法是:

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;
}

[EDIT]

想想看,我相信这在除了宏之外,C++03 中还有一个“类似函数的调用”,原因如下。

一方面,您显然需要模板参数推导来获取数组的大小(直接或通过 sizeof 来获取数组的大小)。但模板参数推导仅适用于函数,不适用于类;即,您可以拥有一个引用类型为 N 的数组的模板参数 R,其中 N 是另一个模板参数,但您必须在调用时同时提供 R 和 N;如果你想从 R 中推导出 N,只有函数调用可以做到这一点。

另一方面,涉及函数调用的任何表达式可以保持常量的唯一方式是当它位于 sizeof 内部时。其他任何事情(例如,访问函数返回值上的静态或枚举成员)仍然需要进行函数调用,这显然意味着这不会是常量表达式。

The best I can think of is this:

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];

which has to be used with another sizeof:

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

[EDIT]

Come to think of it, I believe this is provably impossible to do in a single "function-like call" in C++03, apart from macros, and here's why.

On one hand, you will clearly need template parameter deduction to obtain size of array (either directly, or via sizeof as you do). However, template parameter deduction is only applicable to functions, and not to classes; i.e. you can have a template parameter R of type reference-to-array-of-N, where N is another template parameter, but you'll have to provide both R and N at the point of the call; if you want to deduce N from R, only a function call can do that.

On the other hand, the only way any expression involving a function call can be constant is when it's inside sizeof. Anything else (e.g. accessing a static or enum member on return value of function) still requires the function call to occur, which obviously means this won't be a constant expression.

眼眸里的快感 2024-08-13 02:27:02

这并不完全是您正在寻找的内容,但它很接近 - 来自 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 的“不完美的 C++” 书中还讨论了以下技术:在这里使用。

It's not exactly what you're looking for, but it's close - a snippet from winnt.h which includes some explanation of what the #$%^ it's doing:

//
// 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)))

The RTL_NUMBER_OF_V2() macro ends up being used in the more readable ARRAYSIZE() macro.

Matthew Wilson's "Imperfect C++" book also has a discussion of the techniques that are used here.

七度光 2024-08-13 02:27:02

我喜欢的问题

Adisak的答案

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

: Microsoft 用于 _countof 宏 在 VS2008 中,它有一些不错的功能:

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

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

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

幸运的是,我们不祝你好运。

解决方案

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*);
};

对于那些感兴趣的人,它的工作原理是在标准的基于 sizeof 的数组大小宏之前插入两个“测试”。这些测试不会影响最终计算,但旨在生成非数组类型的编译错误:

  1. 除非 arr 是整数、枚举、指针或数组,否则第一个测试将失败。对于任何其他类型,reinterpret_cast 应该失败。
  2. 对于整型、枚举或指针类型,第二个测试失败。

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

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

    数组类型将通过,因为采用 T 类型数组的地址
    会给你T (*)[],又名指向数组的指针,而不是指向指针的指针。这意味着 check_type 的模板化版本将不匹配。感谢 SFINAE,编译器将转向 check_type 的非模板化版本,它应该接受任何一对指针。由于非模板化版本的返回类型已完全定义,因此不会产生错误。由于我们现在不处理模板,因此本地类型可以正常工作。

The Problem

I like Adisak's answer:

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

It's what Microsoft uses for the _countof macro in VS2008, and it's got some nice features:

  • It operates at compile time
  • It's typesafe (i.e. it will generate a compile-time error if you give it a pointer, which arrays degrade too all too easily)

But as pointed out by Georg, this approach uses templates, so it's 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
}

Fortunately, we're not out luck.

The Solution

Ivan Johnson came up with a clever approach that wins on all accounts: it's typesafe, compile-time, and works with local 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*);
};

For those who are interested, it works by inserting two "tests" before the standard sizeof-based array-size macro. Those tests don't impact the final calculation, but are designed to generate compile errors for non-array types:

  1. The first test fails unless arr is integral, enum, pointer, or array. reinterpret_cast<const T*> should fail for any other types.
  2. The second test fails for integral, enum, or pointer types.

    Integral and enum types will fail because there's no version of check_type that they match, since check_type expects pointers.

    Pointer types will fail because they'll match the templated version of check_type, but the return type (Is_pointer) for the templated check_type is incomplete, which will produce an error.

    Array types will pass because taking the address of an array of type T
    will give you T (*)[], aka a pointer-to-an-array, not a pointer-to-a-pointer. That means that the templated version of check_type won't match. Thanks to SFINAE, the compiler will move on to the non-templated version of check_type, which should accept any pair of pointers. Since the return type for the non-templated version is defined completely, no error will be produced. And since we're not dealing with templates now, local types work fine.

℡Ms空城旧梦 2024-08-13 02:27:02

如果您使用的是仅限 Microsoft 的平台,则可以利用

以下工作正常(VS 2008 RTM),

static int ARRAY[5];
enum ENUM{N=_countof(ARRAY)};

但同样,它是 MS 特定的,因此这可能不适合您。

If you are on a Microsoft only platform, you can take advantage of the _countof macro. This is a non-standard extension which will return the count of elements within an array. It's advantage over most countof style macros is that it will cause a compilation error if it's used on a non-array type.

The following works just fine (VS 2008 RTM)

static int ARRAY[5];
enum ENUM{N=_countof(ARRAY)};

But once again, it's MS specific so this may not work for you.

你丑哭了我 2024-08-13 02:27:02

一般来说,您无法解决它,这就是像 boost array(当然还有 stl 风格的行为)。

You can't solve it in general, thats one the reasons for array wrappers like boost array (plus stl-style behaviour of course).

美胚控场 2024-08-13 02:27:02

如果没有当前 C++ 标准的宏,似乎不可能将 sizeof 数组作为编译时常量获取(您需要一个函数来推断数组大小,但在需要编译时常量的地方不允许函数调用) 。 [编辑:但是看看 Minaev 的出色解决方案!]

但是,您的模板版本也不是类型安全的,并且遇到与宏相同的问题:它也接受指针,特别是衰减为指针的数组。当它接受指针时,sizeof(T*) / sizeof(T) 的结果没有意义。

更好的:

template <typename T, size_t N>
size_t sizeof_array(T (&)[N]){
    return N;
}

It appears not to be possible to obtain the sizeof array as a compile-time constant without a macro with current C++ standard (you need a function to deduce the array size, but function calls are not allowed where you need a compile-time constant). [Edit: But see Minaev's brilliant solution!]

However, your template version isn't typesafe either and suffers from the same problem as the macro: it also accepts pointers and notably arrays decayed to a pointer. When it accepts a pointer, the result of sizeof(T*) / sizeof(T) cannot be meaningful.

Better:

template <typename T, size_t N>
size_t sizeof_array(T (&)[N]){
    return N;
}
缱倦旧时光 2024-08-13 02:27:02

如果没有 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;
}

它要么给你一个可以传递变量的函数,要么给你一个也可以传递类型的模板。您不能将该函数用作编译时常量,但大多数情况下您知道该类型,即使仅作为模板参数。

Without C++0x, the closest I can get is:

#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;
}

which either gives you a function you can pass the variable to, or a template you can pass the type too. You can't use the function for a compile time constant, but most cases you know the type, even if only as template parameter.

猥︴琐丶欲为 2024-08-13 02:27:02

现在,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

Now STL libraries are available to decide/select array size compile time

#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);
}

Output:
3

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文