# using arrays with the STL

This topic is 3188 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

From time to time, I find myself writing code like this:
#include <algorithm>
#include <iostream>
#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;

int main()
{
char const* test[] = {"Hello", "Beautiful", "World"};
std::for_each(test, test + 3, std::cout << _1 << "\n"); // interesting line
}


How do I make sure the expression "test + 3" actually marks the end of the array? After some thinking, I wrote the following pair of inline function templates:
#include <algorithm>
#include <iostream>
#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;

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

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

int main()
{
char const* test[] = {"Hello", "Beautiful", "World"};
std::for_each(test, end_of(test), std::cout << _1 << "\n");
}


Now I'm almost 100% sure that hundreds of other programmers have discovered that trick before me, but I still wanted to share it with you and ask your opinion about it. On a sidenote, do you think it's good practice to write these short const/non-const function pairs with repeated code, or should one of them be implemented in terms of the other? I seem to remember Scott Meyers had an item on that very subject. The code would then look something like this:
template<typename T, size_t n>
inline T const* end_of(T const (&a)[n])
{
return a + n;
}

template<typename T, size_t n>
inline T* end_of(T (&a)[n])
{
return const_cast<T*>(end_of(static_cast<T const (&)[n]>(a)));
}


Would you consider this good practice or over-engineering?

##### Share on other sites
Quote:
 Now I'm almost 100% sure that hundreds of other programmers have discovered that trick before me, but I still wanted to share it with you and ask your opinion about it.

I have a pair of functions, begin() and end(), I think I stole the idea from ToohrVyk. I haven't had cause to overload them in a non-const fashion, I rarely use arrays at all.

Since they are one liners, I would just copy/paste and remove the "const". I mean, your alternative is even longer than the original! Not to mention harder to read. It might be a good idea if the actual logic was quite complex however.

I don't think there is a clean way to implement one in terms of the other. I would be fascinated if someone has a really neat way to do this.

##### Share on other sites
Hm, I just realized there is no need for the const version, the non-const version should be enough:

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

If I pass an array-of-const, the return type will be pointer-to-const automatically.

Quote:
 Original post by rip-offI rarely use arrays at all.

With C++0x's std::initializer_list facility, I guess I won't use them anymore either :)

##### Share on other sites
That appears to be how I had it written.

I would still be interested in learning if there is a cleaner way of handling duplicate code where the only difference is "const". Of particular interest would be things like operator[], where you have the entire class would be templated so you can't take advantage of that trick.

##### Share on other sites
It seems you're already using boost.

Boost already provides that functionality: boost::end(). This will actually work on any container or range.

Boost also provides these syntaxes
boost::for_each(test, std::cout << _1 << "\n");

and
BOOST_FOREACH(const char* s, test)    std::cout << s << "\n";

##### Share on other sites
As for const, it's simply not needed. In your template functions, const can already match as part of T.

##### Share on other sites
Quote:
 Original post by loufoqueBoost already provides that functionality: boost::end().

Awesome, didn't know about that. Thanks!

##### Share on other sites
Quote:
 Original post by loufoqueboost::for_each(test, std::cout << _1 << "\n");

Which header do I need to include for that?

##### Share on other sites
AFAIK Boost doesn't provide it. You need to #define it yourself ("#define foreach BOOST_FOREACH" will do).

##### Share on other sites
Nah, boost::foreach is nicer than that. It lets you write stuff like

BOOST_FOREACH(int i, test){    std::cout<< i << "\n";}

It avoids a bunch of problem stuff with boost::lambda - like the syntax for calling a member function on a bound variable.

##### Share on other sites
Quote:
Original post by DevFred
Quote:
 Original post by loufoqueboost::for_each(test, std::cout << _1 << "\n");

Which header do I need to include for that?

Actually, it seems this is not in the current release. It has been accepted though.

See the updated Boost.Range documentation: http://www.cs.aau.dk/~nesotto/boost/libs/range/
"Range Adaptors" and "Range Algorithms" are the new parts.