using arrays with the STL

Started by
9 comments, last by loufoque 14 years, 11 months ago
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?
Advertisement
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.
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-off
I rarely use arrays at all.

With C++0x's std::initializer_list facility, I guess I won't use them anymore either :)
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.
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";
As for const, it's simply not needed. In your template functions, const can already match as part of T.
Quote:Original post by loufoque
Boost already provides that functionality: boost::end().

Awesome, didn't know about that. Thanks!
Quote:Original post by loufoque
boost::for_each(test, std::cout << _1 << "\n");


Which header do I need to include for that?
AFAIK Boost doesn't provide it. You need to #define it yourself ("#define foreach BOOST_FOREACH" will do).
NextWar: The Quest for Earth available now for Windows Phone 7.
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.

[size=1]Visit my website, rawrrawr.com

This topic is closed to new replies.

Advertisement