13

I use the following template to obtain a pointer pointing after the last element of an array:

template <typename T, size_t n>
T* end_of(T (&array)[n])
{
    return array + n;
}

Now I seem to remember that there was some problem with this approach, but I cannot remember what it was. I believe it had something to with the choice of the type parameters or function parameters, but I'm not sure. So just as a sanity check, do you see any problems with the above code? Small usage test:

int test[] = {11, 19, 5, 17, 7, 3, 13, 2};
std::sort(test, end_of(test));
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • Since you have to pass in the size of the array to the function, what are you really getting out of this that you couldn't get by using the `array` template? – Zac Howland Jan 21 '11 at 13:07
  • @Zac: I don't, the function only has a single parameter. The template parameter `n` is automatically deduced, you just say `end_of(array)` as seen in the example code. – fredoverflow Jan 21 '11 at 13:10
  • In that case, the problem you would run into is dealing with dynamic arrays since `n` would only be the size of the a single `T`. – Zac Howland Jan 21 '11 at 13:17
  • @Zac: No. You cannot pass a pointer to the first element of an array to the template, because a pointer is not an array. – fredoverflow Jan 21 '11 at 13:18
  • Which is exactly the problem I was stating: you cannot use this with dynamically allocated arrays. – Zac Howland Jan 21 '11 at 13:20
  • @Zac: Not *exactly*... you were stating a runtime problem (wrong value), but it actually would be a compile-time "problem". Of course you are right in the sense that the template does not work with dynamic arrays, but since no exception-safety aware C++ programmer would ever use dynamic arrays in user code anyway, this "problem" doesn't affect me. – fredoverflow Jan 21 '11 at 13:26
  • I made no distinction between runtime and compile-time (it is actually a problem in both, but that is neither here nor there). That is the only issue you'd run into with this template. Though, I still say you'd be better off using the `std::array` template class and avoiding the need to create the `end_of` function altogether. – Zac Howland Jan 21 '11 at 13:30
  • @Zac Howland: there is a weird quirky way of dynamically allocating an array (of type array, not pointer) that abuses `new []`: `typedef int array[10]; array* a = new array[1];` Which is basically the same as: `new int array[1][10];`. Now while this represents a dynamically allocated (at runtime), the size of the array still has to be a compile time constant, as all but the first dimension in the `new []` must be compile time constants. That dynamically allocated array could be passed to the template above as `end_of( *a )`. Note again, that we are dealing with a pointer to the array. – David Rodríguez - dribeas Jan 21 '11 at 14:10
  • For those who are using C++11, they can directly use the library function [std::end](http://en.cppreference.com/w/cpp/iterator/end). – legends2k Oct 07 '13 at 02:43

3 Answers3

8

Your proposal is not necessarily evaluated at compile time, it depends on optimisation. The following is calculated at compile time:

template <typename T, size_t N> char (&array(T(&)[N]))[N];

int main()
{
  int myArray[10];

  std::cout << sizeof array(myArray) << std::endl;

  return 0;
}

It works by creating an array type of char which is the same number of elements as the given array. sizeof always returns size in number of chars.

T33C
  • 4,341
  • 2
  • 20
  • 42
2

The only problem i see is that if you ever don't know the length at compile time, your template won't know what to put in there. So you'd have to say test+x or something anyway, and now you have two different ways to do the same thing.

Personally i'd rather just use a vector<int> and thus have end() already defined for me. If you ever need the array, it's available as &v[0].

cHao
  • 84,970
  • 20
  • 145
  • 172
  • 1
    Usually, the compiler knows the size of an array (the only exception is when you declare it as extern without providing the size, and then define the array in a different translation unit). That's the whole point of the template. You cannot pass a pointer to the first element of an array to the template, because a pointer is not an array. – fredoverflow Jan 21 '11 at 13:14
  • 1
    @chao: You can also use `std::array` and have the `end()` function already defined (for statically sized arrays). – Zac Howland Jan 21 '11 at 13:19
  • So, for arrays you use `end_of(a)`, and for pointers you use `a+8`? And of course, for STL containers you use `a.end()`... – cHao Jan 21 '11 at 13:20
  • @CHao - can you add an example of when the compiler wouldn't know the length of an array variable. I can't think of one at the moment. – T33C Jan 21 '11 at 13:53
  • @T33C: Like I already said, if you declare an array as `extern int a[];` in translation unit A and define it as `int a[100];` in translation unit B, then the compiler does not know the size of the array in translation unit A. – fredoverflow Jan 23 '11 at 09:08
1

You need a const version too. However, as far as I know, there's no actual problems with that approach- I see it used commonly.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    No, the above code also works with `const` arrays. if you pass a `const Foo[n]` array, then `T` is deduced to be `const Foo` instead of just `Foo`. – fredoverflow Jan 21 '11 at 13:09
  • 1
    @Fred is right. It only fails for `end_of(A().a);` where `A` is defined by `struct A { int a[1]; };` (because of the rvalueness and the non-const reference), but I think that's not bad but rather a good thing in this case. – Johannes Schaub - litb Jan 21 '11 at 13:47