Sign in to follow this  
DevFred

using arrays with the STL

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 this post


Link to post
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 this post


Link to post
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-off
I rarely use arrays at all.

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

Share this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by loufoque
Boost already provides that functionality: boost::end().

Awesome, didn't know about that. Thanks!

Share this post


Link to post
Share on other sites
Quote:
Original post by loufoque
boost::for_each(test, std::cout << _1 << "\n");


Which header do I need to include for that?

Share this post


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

Share this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by DevFred
Quote:
Original post by loufoque
boost::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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this