As a rule, in C and C++ you should never use an unsigned type such as size_t
to restrict the domain. That's because (1) these languages provide no range checking, and (2) they do provide unreasonable implicit promotions. No range checking means (1) no advantage, and unreasonable implicit promotions means (2) very undesirable disadvantages, so it's plain stupid thing to do: no advantage, very undesirable disadvantages.
However, the standard libraries for these languages do that. They do it for historical reasons only, caught irreversibly in early decisions which at one time made sense. This has both extremely silly consequences such as C99 requiring 17 (!) bits for ptrdiff_t
, and it has the aforementioned extremely undesirable consequences such as using inordinately much time on hunting down bugs resulting from implicit promotions (etc.). For example, in C++ you are practically guaranteed that std::string( "bah!" ).length() < -5
– which can easily trip you up and anyway is as silly as it is possible to design.
Now, you can't infuse new member functions in std::vector
, but you can add a freestanding function. A good name is countOf
. Template it so that it can be applied to just about anything (raw arrays, vectors, etc.).
The triad of functions startOf
, endOf
and countOf
were, as far as I know, first identified by Dietmar Kuehl. C++0x will have std::begin
and std::end
, but AFAIK no corresponding std::size
. In the meantime you can just define this support, which allows you to treat any kinds of container plus raw arrays the same.
An example implementation & further discussion is provided at my blog.
EDIT Adding some code, because it's requested in the comments.
Detection of suitable iterator type:
template< typename Type >
struct It
{
typedef typename Type::iterator T;
};
template< typename Type >
struct It< Type const >
{
typedef typename Type::const_iterator T;
};
template< typename ElemType, Size N >
struct It< ElemType[N] >
{
typedef ElemType* T;
};
And the countOf
, startOf
and endOf
functions, using that deduced iterator type:
template< typename T >
inline Size countOf( T const& c ) { return static_cast<Size>( c.size() ); }
template< typename T, Size N >
inline Size countOf( T (&)[N] ) { return N; }
template< typename T >
inline typename It<T>::T startOf( T& c ) { return c.begin(); }
template< typename T, Size N >
inline T* startOf( T (&a)[N] ) { return a; }
template< typename T >
inline typename It<T>::T endOf( T& c ) { return c.end(); }
template< typename T, Size N >
inline T* endOf( T (&a)[N] ) { return a + N; }
where Size
is a typedef for ptrdiff_t
.
Note: in 64-bit Windows int
(and even long
) is 32-bit. Hence, int
is in general not sufficient for a really large array. ptrdiff_t
is guaranteed to be able to represent the difference between any two pointers, when that difference is well-defined.
Cheers & hth.