How Would You Get the Count of an Array in C++?

来源:互联网 发布:北京租房 知乎 编辑:程序博客网 时间:2024/05/29 17:10

The question is simple: given a C++ array (e.g. x as in int x[10]), how would you getthe number of elements in it?

 

An obvious solution is the following macro (definition 1):

 

#define countof( array ) ( sizeof( array )/sizeof( array[0] ) )

 

I cannot say this isn’t correct, because it does give theright answer when you give it an array. However, the same expression gives you something bogus when you supplysomething that is not an array.  Forexample, if you have

 

    int * p;

 

then countof( p ) always give you 1 on a machine where an int pointer and an int havethe same size (e.g. on a Win32 platform).

 

This macro also wrongfully accepts any object of a classthat has a member function operator[].  For example, suppose you write

 

class IntArray

{

private:

    int * p;

    size_t size;

 

public:

    int & operator [] ( size_t i );

} x;

 

then sizeof( x ) will be the size of the x object, not thesize of the buffer pointed to by x.p.  Therefore you won’t get a correct answer by countof( x ).

 

So we conclude thatdefinition 1 is not good because the compiler does not prevent you frommisusing it.  It fails to enforce thatonly an array can be passed in.

 

What is a better option?

 

Well, if we want the compilerto ensure that the parameter to countof is always an array, we have to find a context where only an array is allowed.  The same context should reject any non-arrayexpression.

 

Some beginners may try this (definition 2):

 

template <typename T, size_tN>

size_t countof( T array[N] )

{

    return N;

}

 

They figure, this templatefunction will accept an array of Nelements and return N.

 

Unfortunately, this doesn’tcompile because C++ treats an arrayparameter the same as a pointer parameter, i.e. the above definition isequivalent to:

 

template <typename T, size_tN>

size_t countof( T * array )

{

    return N;

}

 

It now becomes obvious thatthe function body has no way of knowing what N is.

 

However, if a functionexpects an array reference, then thecompiler does make sure that the size of the actual parameter matches thedeclaration.  This means we can makedefinition 2 work with a minor modification (definition 3):

 

template <typename T, size_tN>

size_t countof( T (&array)[N] )

{

    return N;

}

 

This countof works very well and youcannot fool it by giving it a pointer. However, it is a function, nota macro.  This means you cannot use it where a compile time constant is expected.  In particular, you cannot write something like:

 

int x[10];

int y[ 2*countof(x) ]; // twice as big asx

 

Can we do anything about it?

 

Someone (I don’t know who itis – I just saw it in a piece of code from an unknown author) came up with aclever idea: moving N from the body of thefunction to the return type (e.g. make the function return an array ofN elements), then we can getthe value of N without actually calling the function.

 

To be precise, we have tomake the function return an arrayreference, as C++ does not allow you to return an array directly.

 

The implementation of thisis:

 

template <typename T, size_tN>

char ( &_ArraySizeHelper( T (&array)[N] ))[N];

 

#define countof( array ) (sizeof( _ArraySizeHelper( array ) ))

 

Admittedly, the syntax looksawful.  Indeed, some explanation isnecessary.

 

First, the top-level stuff

 

char ( &_ArraySizeHelper( ... ))[N];

 

says “_ArraySizeHelper is afunction that returns a reference (note the &) to a char array of N elements”.

 

Next, the function parameter is

 

T (&array)[N]

 

which is a reference to a T arrayof N elements.

 

Finally, countof is defined as thesize of the result of the function _ArraySizeHelper.  Note we don’t even need to define _ArraySizeHelper(), -- a declaration is enough.

 

With this new definition,

 

int x[10];

int y[ 2*countof(x) ]; // twice as big asx

 

becomes valid, just as we desire.

 

Am I happy now? Well, I think this definition is definitely better than the others wehave visited, but it is still not quite what I want.  For one thing, it doesn’t work with types defined inside a function.  That’s because the template function _ArraySizeHelper expects a type that is accessible in the global scope.

 

I don’t have a better solution.  If you know one, please let me know.

 

BTW, how does one solve the same problem for C#?  It’s trivial there.  You just say “x.length()”(I forgot the actual syntax, but you should get the idea).

 

One thing to point out is that, in C# the count of anarray is not in its type.  You decide how many elements are there whenyou create the array at run time. Therefore, don’t even try to derive this information at compile time.

原创粉丝点击